diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml new file mode 100644 index 0000000..0a43cc5 --- /dev/null +++ b/.gitea/workflows/release.yml @@ -0,0 +1,79 @@ +name: release + +on: + push: + tags: + - 'v*' + workflow_dispatch: + inputs: + tag: + description: 'Release tag, 例如 v0.1.0(会自动创建 tag 并发布 release)' + required: true + type: string + +jobs: + build: + # 需要一台带 Windows 的自托管 act_runner(Gitea 没有官方 hosted Windows runner) + # 默认按 act_runner 常见标签 windows-latest;按你的 runner 实际 label 调整 + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + + - name: Resolve release tag + shell: pwsh + run: | + if ($env:GITHUB_EVENT_NAME -eq 'workflow_dispatch') { + $t = '${{ inputs.tag }}' + } else { + $t = $env:GITHUB_REF_NAME + } + echo "RELEASE_TAG=$t" >> $env:GITHUB_ENV + + - uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - name: Install dependencies + run: pip install -r requirements.txt + + - name: Build exe + run: pyinstaller --onefile --uac-admin --console --name df-scope-hold script.py + + - name: Package release zip + shell: pwsh + run: | + New-Item -ItemType Directory -Force -Path release | Out-Null + Copy-Item dist/df-scope-hold.exe release/ + Copy-Item config.ini release/ + Copy-Item README.md release/ + Compress-Archive -Path release/* -DestinationPath "df-scope-hold-$env:RELEASE_TAG.zip" -Force + echo "ASSET_ZIP=df-scope-hold-$env:RELEASE_TAG.zip" >> $env:GITHUB_ENV + + - name: Upload workflow artifact + uses: actions/upload-artifact@v3 + with: + name: df-scope-hold-${{ env.RELEASE_TAG }} + path: ${{ env.ASSET_ZIP }} + + - name: Extract release notes from CHANGELOG.md + shell: pwsh + run: | + $content = Get-Content CHANGELOG.md -Raw + $escaped = [regex]::Escape($env:RELEASE_TAG) + $pattern = "(?ms)^## \[$escaped\].*?(?=^## |\Z)" + $m = [regex]::Match($content, $pattern) + if ($m.Success) { + $body = ($m.Value -replace '^## .*\r?\n', '').Trim() + } else { + $body = "Release $env:RELEASE_TAG" + } + $date = [DateTime]::UtcNow.ToString('yyyy-MM-dd') + $body = "**发布日期**: $date (UTC)`n`n" + $body + Set-Content -Path release-notes.md -Value $body -Encoding utf8 + + - name: Create Gitea Release + uses: akkuman/gitea-release-action@v1 + with: + tag_name: ${{ env.RELEASE_TAG }} + files: ${{ env.ASSET_ZIP }} + body_path: release-notes.md diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..d71e204 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,81 @@ +name: release + +on: + push: + tags: + - 'v*' + workflow_dispatch: + inputs: + tag: + description: 'Release tag, 例如 v0.1.0(会自动创建 tag 并发布 release)' + required: true + type: string + +permissions: + contents: write + +jobs: + build: + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + + - name: Resolve release tag + shell: pwsh + run: | + if ($env:GITHUB_EVENT_NAME -eq 'workflow_dispatch') { + $t = '${{ inputs.tag }}' + } else { + $t = $env:GITHUB_REF_NAME + } + echo "RELEASE_TAG=$t" >> $env:GITHUB_ENV + + - uses: actions/setup-python@v5 + with: + python-version: '3.12' + cache: pip + + - name: Install dependencies + run: pip install -r requirements.txt + + - name: Build exe + run: pyinstaller --onefile --uac-admin --console --name df-scope-hold script.py + + - name: Package release zip + shell: pwsh + run: | + New-Item -ItemType Directory -Force -Path release | Out-Null + Copy-Item dist/df-scope-hold.exe release/ + Copy-Item config.ini release/ + Copy-Item README.md release/ + Compress-Archive -Path release/* -DestinationPath "df-scope-hold-$env:RELEASE_TAG.zip" -Force + echo "ASSET_ZIP=df-scope-hold-$env:RELEASE_TAG.zip" >> $env:GITHUB_ENV + + - name: Upload workflow artifact + uses: actions/upload-artifact@v4 + with: + name: df-scope-hold-${{ env.RELEASE_TAG }} + path: ${{ env.ASSET_ZIP }} + + - name: Extract release notes from CHANGELOG.md + shell: pwsh + run: | + $content = Get-Content CHANGELOG.md -Raw + $escaped = [regex]::Escape($env:RELEASE_TAG) + $pattern = "(?ms)^## \[$escaped\].*?(?=^## |\Z)" + $m = [regex]::Match($content, $pattern) + if ($m.Success) { + $body = ($m.Value -replace '^## .*\r?\n', '').Trim() + } else { + $body = "Release $env:RELEASE_TAG" + } + $date = [DateTime]::UtcNow.ToString('yyyy-MM-dd') + $body = "**发布日期**: $date (UTC)`n`n" + $body + Set-Content -Path release-notes.md -Value $body -Encoding utf8 + + - name: Create GitHub Release + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ env.RELEASE_TAG }} + files: ${{ env.ASSET_ZIP }} + body_path: release-notes.md diff --git a/.gitignore b/.gitignore index 3bbe7b6..56f453a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,9 @@ __pycache__/ *.pyc *.pyo + +# PyInstaller build artifacts +build/ +dist/ +release/ +*.spec diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..002ee35 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,22 @@ +# Changelog + +本项目所有重要变更记录于此文件。格式参考 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),版本号遵循 [Semantic Versioning](https://semver.org/lang/zh-CN/)。 + +> 发布日期由 release workflow 在构建时自动注入到 GitHub/Gitea Release 页面,本文件版本标题不再手填日期。 + +## [Unreleased] + +## [v0.1.0] + +首个正式发布版本。 + +### Added +- 监听鼠标右键按下/松开,自动模拟绑定的"屏息"键(默认 `F12`) +- 进程门控:仅在目标游戏进程运行时激活监听,游戏关闭自动停止,重启自动恢复 +- `config.ini` 配置文件,可自定义按键、按键延迟(毫秒)、目标进程名 +- Windows 单 exe 分发,内嵌 UAC manifest,双击自动请求管理员权限 +- GitHub / Gitea Actions 自动构建并发布 release zip(含 exe + 默认 config.ini) + +### Notes +- 游戏客户端通常以管理员权限运行,本工具必须以同等权限运行才能检测到游戏进程 +- 仅在 Windows 上验证(三角洲行动客户端为 Windows 独占) diff --git a/PortablePython/LICENSE.txt b/PortablePython/LICENSE.txt deleted file mode 100644 index 4059c16..0000000 --- a/PortablePython/LICENSE.txt +++ /dev/null @@ -1,676 +0,0 @@ -A. HISTORY OF THE SOFTWARE -========================== - -Python was created in the early 1990s by Guido van Rossum at Stichting -Mathematisch Centrum (CWI, see https://www.cwi.nl) in the Netherlands -as a successor of a language called ABC. Guido remains Python's -principal author, although it includes many contributions from others. - -In 1995, Guido continued his work on Python at the Corporation for -National Research Initiatives (CNRI, see https://www.cnri.reston.va.us) -in Reston, Virginia where he released several versions of the -software. - -In May 2000, Guido and the Python core development team moved to -BeOpen.com to form the BeOpen PythonLabs team. In October of the same -year, the PythonLabs team moved to Digital Creations, which became -Zope Corporation. In 2001, the Python Software Foundation (PSF, see -https://www.python.org/psf/) was formed, a non-profit organization -created specifically to own Python-related Intellectual Property. -Zope Corporation was a sponsoring member of the PSF. - -All Python releases are Open Source (see https://opensource.org for -the Open Source Definition). Historically, most, but not all, Python -releases have also been GPL-compatible; the table below summarizes -the various releases. - - Release Derived Year Owner GPL- - from compatible? (1) - - 0.9.0 thru 1.2 1991-1995 CWI yes - 1.3 thru 1.5.2 1.2 1995-1999 CNRI yes - 1.6 1.5.2 2000 CNRI no - 2.0 1.6 2000 BeOpen.com no - 1.6.1 1.6 2001 CNRI yes (2) - 2.1 2.0+1.6.1 2001 PSF no - 2.0.1 2.0+1.6.1 2001 PSF yes - 2.1.1 2.1+2.0.1 2001 PSF yes - 2.1.2 2.1.1 2002 PSF yes - 2.1.3 2.1.2 2002 PSF yes - 2.2 and above 2.1.1 2001-now PSF yes - -Footnotes: - -(1) GPL-compatible doesn't mean that we're distributing Python under - the GPL. All Python licenses, unlike the GPL, let you distribute - a modified version without making your changes open source. The - GPL-compatible licenses make it possible to combine Python with - other software that is released under the GPL; the others don't. - -(2) According to Richard Stallman, 1.6.1 is not GPL-compatible, - because its license has a choice of law clause. According to - CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1 - is "not incompatible" with the GPL. - -Thanks to the many outside volunteers who have worked under Guido's -direction to make these releases possible. - - -B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON -=============================================================== - -Python software and documentation are licensed under the -Python Software Foundation License Version 2. - -Starting with Python 3.8.6, examples, recipes, and other code in -the documentation are dual licensed under the PSF License Version 2 -and the Zero-Clause BSD license. - -Some software incorporated into Python is under different licenses. -The licenses are listed with code falling under that license. - - -PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 --------------------------------------------- - -1. This LICENSE AGREEMENT is between the Python Software Foundation -("PSF"), and the Individual or Organization ("Licensee") accessing and -otherwise using this software ("Python") in source or binary form and -its associated documentation. - -2. Subject to the terms and conditions of this License Agreement, PSF hereby -grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, -analyze, test, perform and/or display publicly, prepare derivative works, -distribute, and otherwise use Python alone or in any derivative version, -provided, however, that PSF's License Agreement and PSF's notice of copyright, -i.e., "Copyright (c) 2001 Python Software Foundation; All Rights Reserved" -are retained in Python alone or in any derivative version prepared by Licensee. - -3. In the event Licensee prepares a derivative work that is based on -or incorporates Python or any part thereof, and wants to make -the derivative work available to others as provided herein, then -Licensee hereby agrees to include in any such work a brief summary of -the changes made to Python. - -4. PSF is making Python available to Licensee on an "AS IS" -basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR -IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND -DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS -FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT -INFRINGE ANY THIRD PARTY RIGHTS. - -5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON -FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS -A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, -OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. - -6. This License Agreement will automatically terminate upon a material -breach of its terms and conditions. - -7. Nothing in this License Agreement shall be deemed to create any -relationship of agency, partnership, or joint venture between PSF and -Licensee. This License Agreement does not grant permission to use PSF -trademarks or trade name in a trademark sense to endorse or promote -products or services of Licensee, or any third party. - -8. By copying, installing or otherwise using Python, Licensee -agrees to be bound by the terms and conditions of this License -Agreement. - - -BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0 -------------------------------------------- - -BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1 - -1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an -office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the -Individual or Organization ("Licensee") accessing and otherwise using -this software in source or binary form and its associated -documentation ("the Software"). - -2. Subject to the terms and conditions of this BeOpen Python License -Agreement, BeOpen hereby grants Licensee a non-exclusive, -royalty-free, world-wide license to reproduce, analyze, test, perform -and/or display publicly, prepare derivative works, distribute, and -otherwise use the Software alone or in any derivative version, -provided, however, that the BeOpen Python License is retained in the -Software, alone or in any derivative version prepared by Licensee. - -3. BeOpen is making the Software available to Licensee on an "AS IS" -basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR -IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND -DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS -FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT -INFRINGE ANY THIRD PARTY RIGHTS. - -4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE -SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS -AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY -DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. - -5. This License Agreement will automatically terminate upon a material -breach of its terms and conditions. - -6. This License Agreement shall be governed by and interpreted in all -respects by the law of the State of California, excluding conflict of -law provisions. Nothing in this License Agreement shall be deemed to -create any relationship of agency, partnership, or joint venture -between BeOpen and Licensee. This License Agreement does not grant -permission to use BeOpen trademarks or trade names in a trademark -sense to endorse or promote products or services of Licensee, or any -third party. As an exception, the "BeOpen Python" logos available at -http://www.pythonlabs.com/logos.html may be used according to the -permissions granted on that web page. - -7. By copying, installing or otherwise using the software, Licensee -agrees to be bound by the terms and conditions of this License -Agreement. - - -CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1 ---------------------------------------- - -1. This LICENSE AGREEMENT is between the Corporation for National -Research Initiatives, having an office at 1895 Preston White Drive, -Reston, VA 20191 ("CNRI"), and the Individual or Organization -("Licensee") accessing and otherwise using Python 1.6.1 software in -source or binary form and its associated documentation. - -2. Subject to the terms and conditions of this License Agreement, CNRI -hereby grants Licensee a nonexclusive, royalty-free, world-wide -license to reproduce, analyze, test, perform and/or display publicly, -prepare derivative works, distribute, and otherwise use Python 1.6.1 -alone or in any derivative version, provided, however, that CNRI's -License Agreement and CNRI's notice of copyright, i.e., "Copyright (c) -1995-2001 Corporation for National Research Initiatives; All Rights -Reserved" are retained in Python 1.6.1 alone or in any derivative -version prepared by Licensee. Alternately, in lieu of CNRI's License -Agreement, Licensee may substitute the following text (omitting the -quotes): "Python 1.6.1 is made available subject to the terms and -conditions in CNRI's License Agreement. This Agreement together with -Python 1.6.1 may be located on the internet using the following -unique, persistent identifier (known as a handle): 1895.22/1013. This -Agreement may also be obtained from a proxy server on the internet -using the following URL: http://hdl.handle.net/1895.22/1013". - -3. In the event Licensee prepares a derivative work that is based on -or incorporates Python 1.6.1 or any part thereof, and wants to make -the derivative work available to others as provided herein, then -Licensee hereby agrees to include in any such work a brief summary of -the changes made to Python 1.6.1. - -4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS" -basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR -IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND -DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS -FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT -INFRINGE ANY THIRD PARTY RIGHTS. - -5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON -1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS -A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1, -OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. - -6. This License Agreement will automatically terminate upon a material -breach of its terms and conditions. - -7. This License Agreement shall be governed by the federal -intellectual property law of the United States, including without -limitation the federal copyright law, and, to the extent such -U.S. federal law does not apply, by the law of the Commonwealth of -Virginia, excluding Virginia's conflict of law provisions. -Notwithstanding the foregoing, with regard to derivative works based -on Python 1.6.1 that incorporate non-separable material that was -previously distributed under the GNU General Public License (GPL), the -law of the Commonwealth of Virginia shall govern this License -Agreement only as to issues arising under or with respect to -Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this -License Agreement shall be deemed to create any relationship of -agency, partnership, or joint venture between CNRI and Licensee. This -License Agreement does not grant permission to use CNRI trademarks or -trade name in a trademark sense to endorse or promote products or -services of Licensee, or any third party. - -8. By clicking on the "ACCEPT" button where indicated, or by copying, -installing or otherwise using Python 1.6.1, Licensee agrees to be -bound by the terms and conditions of this License Agreement. - - ACCEPT - - -CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2 --------------------------------------------------- - -Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam, -The Netherlands. All rights reserved. - -Permission to use, copy, modify, and distribute this software and its -documentation for any purpose and without fee is hereby granted, -provided that the above copyright notice appear in all copies and that -both that copyright notice and this permission notice appear in -supporting documentation, and that the name of Stichting Mathematisch -Centrum or CWI not be used in advertising or publicity pertaining to -distribution of the software without specific, written prior -permission. - -STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO -THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE -FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -ZERO-CLAUSE BSD LICENSE FOR CODE IN THE PYTHON DOCUMENTATION ----------------------------------------------------------------------- - -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH -REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, -INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR -OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -PERFORMANCE OF THIS SOFTWARE. - - - -Additional Conditions for this Windows binary build ---------------------------------------------------- - -This program is linked with and uses Microsoft Distributable Code, -copyrighted by Microsoft Corporation. The Microsoft Distributable Code -is embedded in each .exe, .dll and .pyd file as a result of running -the code through a linker. - -If you further distribute programs that include the Microsoft -Distributable Code, you must comply with the restrictions on -distribution specified by Microsoft. In particular, you must require -distributors and external end users to agree to terms that protect the -Microsoft Distributable Code at least as much as Microsoft's own -requirements for the Distributable Code. See Microsoft's documentation -(included in its developer tools and on its website at microsoft.com) -for specific details. - -Redistribution of the Windows binary build of the Python interpreter -complies with this agreement, provided that you do not: - -- alter any copyright, trademark or patent notice in Microsoft's -Distributable Code; - -- use Microsoft's trademarks in your programs' names or in a way that -suggests your programs come from or are endorsed by Microsoft; - -- distribute Microsoft's Distributable Code to run on a platform other -than Microsoft operating systems, run-time technologies or application -platforms; or - -- include Microsoft Distributable Code in malicious, deceptive or -unlawful programs. - -These restrictions apply only to the Microsoft Distributable Code as -defined above, not to Python itself or any programs running on the -Python interpreter. The redistribution of the Python interpreter and -libraries is governed by the Python Software License included with this -file, or by other licenses as marked. - - - --------------------------------------------------------------------------- - -This program, "bzip2", the associated library "libbzip2", and all -documentation, are copyright (C) 1996-2019 Julian R Seward. All -rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. The origin of this software must not be misrepresented; you must - not claim that you wrote the original software. If you use this - software in a product, an acknowledgment in the product - documentation would be appreciated but is not required. - -3. Altered source versions must be plainly marked as such, and must - not be misrepresented as being the original software. - -4. The name of the author may not be used to endorse or promote - products derived from this software without specific prior written - permission. - -THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS -OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE -GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -Julian Seward, jseward@acm.org -bzip2/libbzip2 version 1.0.8 of 13 July 2019 - --------------------------------------------------------------------------- - -libffi - Copyright (c) 1996-2022 Anthony Green, Red Hat, Inc and others. -See source files for details. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -``Software''), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -BSD License - -For Zstandard software - -Copyright (c) Meta Platforms, Inc. and affiliates. All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - * Neither the name Facebook, nor Meta, nor the names of its contributors may - be used to endorse or promote products derived from this software without - specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - - Apache License - Version 2.0, January 2004 - https://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - -This software is copyrighted by the Regents of the University of -California, Sun Microsystems, Inc., Scriptics Corporation, ActiveState -Corporation and other parties. The following terms apply to all files -associated with the software unless explicitly disclaimed in -individual files. - -The authors hereby grant permission to use, copy, modify, distribute, -and license this software and its documentation for any purpose, provided -that existing copyright notices are retained in all copies and that this -notice is included verbatim in any distributions. No written agreement, -license, or royalty fee is required for any of the authorized uses. -Modifications to this software may be copyrighted by their authors -and need not follow the licensing terms described here, provided that -the new terms are clearly indicated on the first page of each file where -they apply. - -IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY -FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY -DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - -THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, -INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE -IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE -NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR -MODIFICATIONS. - -GOVERNMENT USE: If you are acquiring this software on behalf of the -U.S. government, the Government shall have only "Restricted Rights" -in the software and related documentation as defined in the Federal -Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you -are acquiring the software on behalf of the Department of Defense, the -software shall be classified as "Commercial Computer Software" and the -Government shall have only "Restricted Rights" as defined in Clause -252.227-7014 (b) (3) of DFARs. Notwithstanding the foregoing, the -authors grant the U.S. Government and others acting in its behalf -permission to use and distribute the software in accordance with the -terms specified in this license. - -This software is copyrighted by the Regents of the University of -California, Sun Microsystems, Inc., Scriptics Corporation, ActiveState -Corporation, Apple Inc. and other parties. The following terms apply to -all files associated with the software unless explicitly disclaimed in -individual files. - -The authors hereby grant permission to use, copy, modify, distribute, -and license this software and its documentation for any purpose, provided -that existing copyright notices are retained in all copies and that this -notice is included verbatim in any distributions. No written agreement, -license, or royalty fee is required for any of the authorized uses. -Modifications to this software may be copyrighted by their authors -and need not follow the licensing terms described here, provided that -the new terms are clearly indicated on the first page of each file where -they apply. - -IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY -FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY -DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - -THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, -INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE -IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE -NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR -MODIFICATIONS. - -GOVERNMENT USE: If you are acquiring this software on behalf of the -U.S. government, the Government shall have only "Restricted Rights" -in the software and related documentation as defined in the Federal -Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you -are acquiring the software on behalf of the Department of Defense, the -software shall be classified as "Commercial Computer Software" and the -Government shall have only "Restricted Rights" as defined in Clause -252.227-7013 (b) (3) of DFARs. Notwithstanding the foregoing, the -authors grant the U.S. Government and others acting in its behalf -permission to use and distribute the software in accordance with the -terms specified in this license. - diff --git a/PortablePython/Lib/site-packages/psutil-7.0.0.dist-info/INSTALLER b/PortablePython/Lib/site-packages/psutil-7.0.0.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/PortablePython/Lib/site-packages/psutil-7.0.0.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/PortablePython/Lib/site-packages/psutil-7.0.0.dist-info/LICENSE b/PortablePython/Lib/site-packages/psutil-7.0.0.dist-info/LICENSE deleted file mode 100644 index cff5eb7..0000000 --- a/PortablePython/Lib/site-packages/psutil-7.0.0.dist-info/LICENSE +++ /dev/null @@ -1,29 +0,0 @@ -BSD 3-Clause License - -Copyright (c) 2009, Jay Loden, Dave Daeschler, Giampaolo Rodola -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - * Neither the name of the psutil authors nor the names of its contributors - may be used to endorse or promote products derived from this software without - specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/PortablePython/Lib/site-packages/psutil-7.0.0.dist-info/METADATA b/PortablePython/Lib/site-packages/psutil-7.0.0.dist-info/METADATA deleted file mode 100644 index e689c06..0000000 --- a/PortablePython/Lib/site-packages/psutil-7.0.0.dist-info/METADATA +++ /dev/null @@ -1,551 +0,0 @@ -Metadata-Version: 2.1 -Name: psutil -Version: 7.0.0 -Summary: Cross-platform lib for process and system monitoring in Python. NOTE: the syntax of this script MUST be kept compatible with Python 2.7. -Home-page: https://github.com/giampaolo/psutil -Author: Giampaolo Rodola -Author-email: g.rodola@gmail.com -License: BSD-3-Clause -Keywords: ps,top,kill,free,lsof,netstat,nice,tty,ionice,uptime,taskmgr,process,df,iotop,iostat,ifconfig,taskset,who,pidof,pmap,smem,pstree,monitoring,ulimit,prlimit,smem,performance,metrics,agent,observability -Platform: Platform Independent -Classifier: Development Status :: 5 - Production/Stable -Classifier: Environment :: Console -Classifier: Environment :: Win32 (MS Windows) -Classifier: Intended Audience :: Developers -Classifier: Intended Audience :: Information Technology -Classifier: Intended Audience :: System Administrators -Classifier: License :: OSI Approved :: BSD License -Classifier: Operating System :: MacOS :: MacOS X -Classifier: Operating System :: Microsoft :: Windows :: Windows 10 -Classifier: Operating System :: Microsoft :: Windows :: Windows 7 -Classifier: Operating System :: Microsoft :: Windows :: Windows 8 -Classifier: Operating System :: Microsoft :: Windows :: Windows 8.1 -Classifier: Operating System :: Microsoft :: Windows :: Windows Server 2003 -Classifier: Operating System :: Microsoft :: Windows :: Windows Server 2008 -Classifier: Operating System :: Microsoft :: Windows :: Windows Vista -Classifier: Operating System :: Microsoft -Classifier: Operating System :: OS Independent -Classifier: Operating System :: POSIX :: AIX -Classifier: Operating System :: POSIX :: BSD :: FreeBSD -Classifier: Operating System :: POSIX :: BSD :: NetBSD -Classifier: Operating System :: POSIX :: BSD :: OpenBSD -Classifier: Operating System :: POSIX :: BSD -Classifier: Operating System :: POSIX :: Linux -Classifier: Operating System :: POSIX :: SunOS/Solaris -Classifier: Operating System :: POSIX -Classifier: Programming Language :: C -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: Implementation :: CPython -Classifier: Programming Language :: Python :: Implementation :: PyPy -Classifier: Programming Language :: Python -Classifier: Topic :: Software Development :: Libraries :: Python Modules -Classifier: Topic :: Software Development :: Libraries -Classifier: Topic :: System :: Benchmark -Classifier: Topic :: System :: Hardware :: Hardware Drivers -Classifier: Topic :: System :: Hardware -Classifier: Topic :: System :: Monitoring -Classifier: Topic :: System :: Networking :: Monitoring :: Hardware Watchdog -Classifier: Topic :: System :: Networking :: Monitoring -Classifier: Topic :: System :: Networking -Classifier: Topic :: System :: Operating System -Classifier: Topic :: System :: Systems Administration -Classifier: Topic :: Utilities -Requires-Python: >=3.6 -Description-Content-Type: text/x-rst -License-File: LICENSE -Provides-Extra: dev -Requires-Dist: pytest ; extra == 'dev' -Requires-Dist: pytest-xdist ; extra == 'dev' -Requires-Dist: setuptools ; extra == 'dev' -Requires-Dist: pywin32 ; extra == 'dev' -Requires-Dist: wheel ; extra == 'dev' -Requires-Dist: wmi ; extra == 'dev' -Requires-Dist: abi3audit ; extra == 'dev' -Requires-Dist: black ==24.10.0 ; extra == 'dev' -Requires-Dist: check-manifest ; extra == 'dev' -Requires-Dist: coverage ; extra == 'dev' -Requires-Dist: packaging ; extra == 'dev' -Requires-Dist: pylint ; extra == 'dev' -Requires-Dist: pyperf ; extra == 'dev' -Requires-Dist: pypinfo ; extra == 'dev' -Requires-Dist: pytest-cov ; extra == 'dev' -Requires-Dist: requests ; extra == 'dev' -Requires-Dist: rstcheck ; extra == 'dev' -Requires-Dist: ruff ; extra == 'dev' -Requires-Dist: sphinx ; extra == 'dev' -Requires-Dist: sphinx-rtd-theme ; extra == 'dev' -Requires-Dist: toml-sort ; extra == 'dev' -Requires-Dist: twine ; extra == 'dev' -Requires-Dist: virtualenv ; extra == 'dev' -Requires-Dist: vulture ; extra == 'dev' -Requires-Dist: pyreadline ; extra == 'dev' -Requires-Dist: pdbpp ; extra == 'dev' -Provides-Extra: test -Requires-Dist: pytest ; extra == 'test' -Requires-Dist: pytest-xdist ; extra == 'test' -Requires-Dist: setuptools ; extra == 'test' -Requires-Dist: pywin32 ; extra == 'test' -Requires-Dist: wheel ; extra == 'test' -Requires-Dist: wmi ; extra == 'test' - -| |downloads| |stars| |forks| |contributors| |coverage| -| |version| |py-versions| |packages| |license| -| |github-actions-wheels| |github-actions-bsd| |doc| |twitter| |tidelift| - -.. |downloads| image:: https://img.shields.io/pypi/dm/psutil.svg - :target: https://pepy.tech/project/psutil - :alt: Downloads - -.. |stars| image:: https://img.shields.io/github/stars/giampaolo/psutil.svg - :target: https://github.com/giampaolo/psutil/stargazers - :alt: Github stars - -.. |forks| image:: https://img.shields.io/github/forks/giampaolo/psutil.svg - :target: https://github.com/giampaolo/psutil/network/members - :alt: Github forks - -.. |contributors| image:: https://img.shields.io/github/contributors/giampaolo/psutil.svg - :target: https://github.com/giampaolo/psutil/graphs/contributors - :alt: Contributors - -.. |github-actions-wheels| image:: https://img.shields.io/github/actions/workflow/status/giampaolo/psutil/.github/workflows/build.yml.svg?label=Linux%2C%20macOS%2C%20Windows - :target: https://github.com/giampaolo/psutil/actions?query=workflow%3Abuild - :alt: Linux, macOS, Windows - -.. |github-actions-bsd| image:: https://img.shields.io/github/actions/workflow/status/giampaolo/psutil/.github/workflows/bsd.yml.svg?label=FreeBSD,%20NetBSD,%20OpenBSD - :target: https://github.com/giampaolo/psutil/actions?query=workflow%3Absd-tests - :alt: FreeBSD, NetBSD, OpenBSD - -.. |coverage| image:: https://coveralls.io/repos/github/giampaolo/psutil/badge.svg?branch=master - :target: https://coveralls.io/github/giampaolo/psutil?branch=master - :alt: Test coverage (coverall.io) - -.. |doc| image:: https://readthedocs.org/projects/psutil/badge/?version=latest - :target: https://psutil.readthedocs.io/en/latest/ - :alt: Documentation Status - -.. |version| image:: https://img.shields.io/pypi/v/psutil.svg?label=pypi - :target: https://pypi.org/project/psutil - :alt: Latest version - -.. |py-versions| image:: https://img.shields.io/pypi/pyversions/psutil.svg - :alt: Supported Python versions - -.. |packages| image:: https://repology.org/badge/tiny-repos/python:psutil.svg - :target: https://repology.org/metapackage/python:psutil/versions - :alt: Binary packages - -.. |license| image:: https://img.shields.io/pypi/l/psutil.svg - :target: https://github.com/giampaolo/psutil/blob/master/LICENSE - :alt: License - -.. |twitter| image:: https://img.shields.io/twitter/follow/grodola.svg?label=follow&style=flat&logo=twitter&logoColor=4FADFF - :target: https://twitter.com/grodola - :alt: Twitter Follow - -.. |tidelift| image:: https://tidelift.com/badges/github/giampaolo/psutil?style=flat - :target: https://tidelift.com/subscription/pkg/pypi-psutil?utm_source=pypi-psutil&utm_medium=referral&utm_campaign=readme - :alt: Tidelift - ------ - -Quick links -=========== - -- `Home page `_ -- `Install `_ -- `Documentation `_ -- `Download `_ -- `Forum `_ -- `StackOverflow `_ -- `Blog `_ -- `What's new `_ - - -Summary -======= - -psutil (process and system utilities) is a cross-platform library for -retrieving information on **running processes** and **system utilization** -(CPU, memory, disks, network, sensors) in Python. -It is useful mainly for **system monitoring**, **profiling and limiting process -resources** and **management of running processes**. -It implements many functionalities offered by classic UNIX command line tools -such as *ps, top, iotop, lsof, netstat, ifconfig, free* and others. -psutil currently supports the following platforms: - -- **Linux** -- **Windows** -- **macOS** -- **FreeBSD, OpenBSD**, **NetBSD** -- **Sun Solaris** -- **AIX** - -Supported Python versions are cPython 3.6+ and `PyPy `__. -Latest psutil version supporting Python 2.7 is -`psutil 6.1.1 `__. - -Funding -======= - -While psutil is free software and will always be, the project would benefit -immensely from some funding. -Keeping up with bug reports and maintenance has become hardly sustainable for -me alone in terms of time. -If you're a company that's making significant use of psutil you can consider -becoming a sponsor via `GitHub Sponsors `__, -`Open Collective `__ or -`PayPal `__ -and have your logo displayed in here and psutil `doc `__. - -Sponsors -======== - -.. image:: https://github.com/giampaolo/psutil/raw/master/docs/_static/tidelift-logo.png - :width: 200 - :alt: Alternative text - -`Add your logo `__. - -Example usages -============== - -This represents pretty much the whole psutil API. - -CPU ---- - -.. code-block:: python - - >>> import psutil - >>> - >>> psutil.cpu_times() - scputimes(user=3961.46, nice=169.729, system=2150.659, idle=16900.540, iowait=629.59, irq=0.0, softirq=19.42, steal=0.0, guest=0, guest_nice=0.0) - >>> - >>> for x in range(3): - ... psutil.cpu_percent(interval=1) - ... - 4.0 - 5.9 - 3.8 - >>> - >>> for x in range(3): - ... psutil.cpu_percent(interval=1, percpu=True) - ... - [4.0, 6.9, 3.7, 9.2] - [7.0, 8.5, 2.4, 2.1] - [1.2, 9.0, 9.9, 7.2] - >>> - >>> for x in range(3): - ... psutil.cpu_times_percent(interval=1, percpu=False) - ... - scputimes(user=1.5, nice=0.0, system=0.5, idle=96.5, iowait=1.5, irq=0.0, softirq=0.0, steal=0.0, guest=0.0, guest_nice=0.0) - scputimes(user=1.0, nice=0.0, system=0.0, idle=99.0, iowait=0.0, irq=0.0, softirq=0.0, steal=0.0, guest=0.0, guest_nice=0.0) - scputimes(user=2.0, nice=0.0, system=0.0, idle=98.0, iowait=0.0, irq=0.0, softirq=0.0, steal=0.0, guest=0.0, guest_nice=0.0) - >>> - >>> psutil.cpu_count() - 4 - >>> psutil.cpu_count(logical=False) - 2 - >>> - >>> psutil.cpu_stats() - scpustats(ctx_switches=20455687, interrupts=6598984, soft_interrupts=2134212, syscalls=0) - >>> - >>> psutil.cpu_freq() - scpufreq(current=931.42925, min=800.0, max=3500.0) - >>> - >>> psutil.getloadavg() # also on Windows (emulated) - (3.14, 3.89, 4.67) - -Memory ------- - -.. code-block:: python - - >>> psutil.virtual_memory() - svmem(total=10367352832, available=6472179712, percent=37.6, used=8186245120, free=2181107712, active=4748992512, inactive=2758115328, buffers=790724608, cached=3500347392, shared=787554304) - >>> psutil.swap_memory() - sswap(total=2097147904, used=296128512, free=1801019392, percent=14.1, sin=304193536, sout=677842944) - >>> - -Disks ------ - -.. code-block:: python - - >>> psutil.disk_partitions() - [sdiskpart(device='/dev/sda1', mountpoint='/', fstype='ext4', opts='rw,nosuid'), - sdiskpart(device='/dev/sda2', mountpoint='/home', fstype='ext', opts='rw')] - >>> - >>> psutil.disk_usage('/') - sdiskusage(total=21378641920, used=4809781248, free=15482871808, percent=22.5) - >>> - >>> psutil.disk_io_counters(perdisk=False) - sdiskio(read_count=719566, write_count=1082197, read_bytes=18626220032, write_bytes=24081764352, read_time=5023392, write_time=63199568, read_merged_count=619166, write_merged_count=812396, busy_time=4523412) - >>> - -Network -------- - -.. code-block:: python - - >>> psutil.net_io_counters(pernic=True) - {'eth0': netio(bytes_sent=485291293, bytes_recv=6004858642, packets_sent=3251564, packets_recv=4787798, errin=0, errout=0, dropin=0, dropout=0), - 'lo': netio(bytes_sent=2838627, bytes_recv=2838627, packets_sent=30567, packets_recv=30567, errin=0, errout=0, dropin=0, dropout=0)} - >>> - >>> psutil.net_connections(kind='tcp') - [sconn(fd=115, family=, type=, laddr=addr(ip='10.0.0.1', port=48776), raddr=addr(ip='93.186.135.91', port=80), status='ESTABLISHED', pid=1254), - sconn(fd=117, family=, type=, laddr=addr(ip='10.0.0.1', port=43761), raddr=addr(ip='72.14.234.100', port=80), status='CLOSING', pid=2987), - ...] - >>> - >>> psutil.net_if_addrs() - {'lo': [snicaddr(family=, address='127.0.0.1', netmask='255.0.0.0', broadcast='127.0.0.1', ptp=None), - snicaddr(family=, address='::1', netmask='ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', broadcast=None, ptp=None), - snicaddr(family=, address='00:00:00:00:00:00', netmask=None, broadcast='00:00:00:00:00:00', ptp=None)], - 'wlan0': [snicaddr(family=, address='192.168.1.3', netmask='255.255.255.0', broadcast='192.168.1.255', ptp=None), - snicaddr(family=, address='fe80::c685:8ff:fe45:641%wlan0', netmask='ffff:ffff:ffff:ffff::', broadcast=None, ptp=None), - snicaddr(family=, address='c4:85:08:45:06:41', netmask=None, broadcast='ff:ff:ff:ff:ff:ff', ptp=None)]} - >>> - >>> psutil.net_if_stats() - {'lo': snicstats(isup=True, duplex=, speed=0, mtu=65536, flags='up,loopback,running'), - 'wlan0': snicstats(isup=True, duplex=, speed=100, mtu=1500, flags='up,broadcast,running,multicast')} - >>> - -Sensors -------- - -.. code-block:: python - - >>> import psutil - >>> psutil.sensors_temperatures() - {'acpitz': [shwtemp(label='', current=47.0, high=103.0, critical=103.0)], - 'asus': [shwtemp(label='', current=47.0, high=None, critical=None)], - 'coretemp': [shwtemp(label='Physical id 0', current=52.0, high=100.0, critical=100.0), - shwtemp(label='Core 0', current=45.0, high=100.0, critical=100.0)]} - >>> - >>> psutil.sensors_fans() - {'asus': [sfan(label='cpu_fan', current=3200)]} - >>> - >>> psutil.sensors_battery() - sbattery(percent=93, secsleft=16628, power_plugged=False) - >>> - -Other system info ------------------ - -.. code-block:: python - - >>> import psutil - >>> psutil.users() - [suser(name='giampaolo', terminal='pts/2', host='localhost', started=1340737536.0, pid=1352), - suser(name='giampaolo', terminal='pts/3', host='localhost', started=1340737792.0, pid=1788)] - >>> - >>> psutil.boot_time() - 1365519115.0 - >>> - -Process management ------------------- - -.. code-block:: python - - >>> import psutil - >>> psutil.pids() - [1, 2, 3, 4, 5, 6, 7, 46, 48, 50, 51, 178, 182, 222, 223, 224, 268, 1215, - 1216, 1220, 1221, 1243, 1244, 1301, 1601, 2237, 2355, 2637, 2774, 3932, - 4176, 4177, 4185, 4187, 4189, 4225, 4243, 4245, 4263, 4282, 4306, 4311, - 4312, 4313, 4314, 4337, 4339, 4357, 4358, 4363, 4383, 4395, 4408, 4433, - 4443, 4445, 4446, 5167, 5234, 5235, 5252, 5318, 5424, 5644, 6987, 7054, - 7055, 7071] - >>> - >>> p = psutil.Process(7055) - >>> p - psutil.Process(pid=7055, name='python3', status='running', started='09:04:44') - >>> p.pid - 7055 - >>> p.name() - 'python3' - >>> p.exe() - '/usr/bin/python3' - >>> p.cwd() - '/home/giampaolo' - >>> p.cmdline() - ['/usr/bin/python3', 'main.py'] - >>> - >>> p.ppid() - 7054 - >>> p.parent() - psutil.Process(pid=4699, name='bash', status='sleeping', started='09:06:44') - >>> p.parents() - [psutil.Process(pid=4699, name='bash', started='09:06:44'), - psutil.Process(pid=4689, name='gnome-terminal-server', status='sleeping', started='0:06:44'), - psutil.Process(pid=1, name='systemd', status='sleeping', started='05:56:55')] - >>> p.children(recursive=True) - [psutil.Process(pid=29835, name='python3', status='sleeping', started='11:45:38'), - psutil.Process(pid=29836, name='python3', status='waking', started='11:43:39')] - >>> - >>> p.status() - 'running' - >>> p.create_time() - 1267551141.5019531 - >>> p.terminal() - '/dev/pts/0' - >>> - >>> p.username() - 'giampaolo' - >>> p.uids() - puids(real=1000, effective=1000, saved=1000) - >>> p.gids() - pgids(real=1000, effective=1000, saved=1000) - >>> - >>> p.cpu_times() - pcputimes(user=1.02, system=0.31, children_user=0.32, children_system=0.1, iowait=0.0) - >>> p.cpu_percent(interval=1.0) - 12.1 - >>> p.cpu_affinity() - [0, 1, 2, 3] - >>> p.cpu_affinity([0, 1]) # set - >>> p.cpu_num() - 1 - >>> - >>> p.memory_info() - pmem(rss=10915840, vms=67608576, shared=3313664, text=2310144, lib=0, data=7262208, dirty=0) - >>> p.memory_full_info() # "real" USS memory usage (Linux, macOS, Win only) - pfullmem(rss=10199040, vms=52133888, shared=3887104, text=2867200, lib=0, data=5967872, dirty=0, uss=6545408, pss=6872064, swap=0) - >>> p.memory_percent() - 0.7823 - >>> p.memory_maps() - [pmmap_grouped(path='/lib/x8664-linux-gnu/libutil-2.15.so', rss=32768, size=2125824, pss=32768, shared_clean=0, shared_dirty=0, private_clean=20480, private_dirty=12288, referenced=32768, anonymous=12288, swap=0), - pmmap_grouped(path='/lib/x8664-linux-gnu/libc-2.15.so', rss=3821568, size=3842048, pss=3821568, shared_clean=0, shared_dirty=0, private_clean=0, private_dirty=3821568, referenced=3575808, anonymous=3821568, swap=0), - pmmap_grouped(path='[heap]', rss=32768, size=139264, pss=32768, shared_clean=0, shared_dirty=0, private_clean=0, private_dirty=32768, referenced=32768, anonymous=32768, swap=0), - pmmap_grouped(path='[stack]', rss=2465792, size=2494464, pss=2465792, shared_clean=0, shared_dirty=0, private_clean=0, private_dirty=2465792, referenced=2277376, anonymous=2465792, swap=0), - ...] - >>> - >>> p.io_counters() - pio(read_count=478001, write_count=59371, read_bytes=700416, write_bytes=69632, read_chars=456232, write_chars=517543) - >>> - >>> p.open_files() - [popenfile(path='/home/giampaolo/monit.py', fd=3, position=0, mode='r', flags=32768), - popenfile(path='/var/log/monit.log', fd=4, position=235542, mode='a', flags=33793)] - >>> - >>> p.net_connections(kind='tcp') - [pconn(fd=115, family=, type=, laddr=addr(ip='10.0.0.1', port=48776), raddr=addr(ip='93.186.135.91', port=80), status='ESTABLISHED'), - pconn(fd=117, family=, type=, laddr=addr(ip='10.0.0.1', port=43761), raddr=addr(ip='72.14.234.100', port=80), status='CLOSING')] - >>> - >>> p.threads() - [pthread(id=5234, user_time=22.5, system_time=9.2891), - pthread(id=5237, user_time=0.0707, system_time=1.1)] - >>> - >>> p.num_threads() - 4 - >>> p.num_fds() - 8 - >>> p.num_ctx_switches() - pctxsw(voluntary=78, involuntary=19) - >>> - >>> p.nice() - 0 - >>> p.nice(10) # set - >>> - >>> p.ionice(psutil.IOPRIO_CLASS_IDLE) # IO priority (Win and Linux only) - >>> p.ionice() - pionice(ioclass=, value=0) - >>> - >>> p.rlimit(psutil.RLIMIT_NOFILE, (5, 5)) # set resource limits (Linux only) - >>> p.rlimit(psutil.RLIMIT_NOFILE) - (5, 5) - >>> - >>> p.environ() - {'LC_PAPER': 'it_IT.UTF-8', 'SHELL': '/bin/bash', 'GREP_OPTIONS': '--color=auto', - 'XDG_CONFIG_DIRS': '/etc/xdg/xdg-ubuntu:/usr/share/upstart/xdg:/etc/xdg', - ...} - >>> - >>> p.as_dict() - {'status': 'running', 'num_ctx_switches': pctxsw(voluntary=63, involuntary=1), 'pid': 5457, ...} - >>> p.is_running() - True - >>> p.suspend() - >>> p.resume() - >>> - >>> p.terminate() - >>> p.kill() - >>> p.wait(timeout=3) - - >>> - >>> psutil.test() - USER PID %CPU %MEM VSZ RSS TTY START TIME COMMAND - root 1 0.0 0.0 24584 2240 Jun17 00:00 init - root 2 0.0 0.0 0 0 Jun17 00:00 kthreadd - ... - giampaolo 31475 0.0 0.0 20760 3024 /dev/pts/0 Jun19 00:00 python2.4 - giampaolo 31721 0.0 2.2 773060 181896 00:04 10:30 chrome - root 31763 0.0 0.0 0 0 00:05 00:00 kworker/0:1 - >>> - -Further process APIs --------------------- - -.. code-block:: python - - >>> import psutil - >>> for proc in psutil.process_iter(['pid', 'name']): - ... print(proc.info) - ... - {'pid': 1, 'name': 'systemd'} - {'pid': 2, 'name': 'kthreadd'} - {'pid': 3, 'name': 'ksoftirqd/0'} - ... - >>> - >>> psutil.pid_exists(3) - True - >>> - >>> def on_terminate(proc): - ... print("process {} terminated".format(proc)) - ... - >>> # waits for multiple processes to terminate - >>> gone, alive = psutil.wait_procs(procs_list, timeout=3, callback=on_terminate) - >>> - -Windows services ----------------- - -.. code-block:: python - - >>> list(psutil.win_service_iter()) - [, - , - , - , - ...] - >>> s = psutil.win_service_get('alg') - >>> s.as_dict() - {'binpath': 'C:\\Windows\\System32\\alg.exe', - 'description': 'Provides support for 3rd party protocol plug-ins for Internet Connection Sharing', - 'display_name': 'Application Layer Gateway Service', - 'name': 'alg', - 'pid': None, - 'start_type': 'manual', - 'status': 'stopped', - 'username': 'NT AUTHORITY\\LocalService'} - -Projects using psutil -===================== - -Here's some I find particularly interesting: - -- https://github.com/google/grr -- https://github.com/facebook/osquery/ -- https://github.com/nicolargo/glances -- https://github.com/aristocratos/bpytop -- https://github.com/Jahaja/psdash -- https://github.com/ajenti/ajenti -- https://github.com/home-assistant/home-assistant/ - -Portings -======== - -- Go: https://github.com/shirou/gopsutil -- C: https://github.com/hamon-in/cpslib -- Rust: https://github.com/rust-psutil/rust-psutil -- Nim: https://github.com/johnscillieri/psutil-nim - diff --git a/PortablePython/Lib/site-packages/psutil-7.0.0.dist-info/RECORD b/PortablePython/Lib/site-packages/psutil-7.0.0.dist-info/RECORD deleted file mode 100644 index 294bafc..0000000 --- a/PortablePython/Lib/site-packages/psutil-7.0.0.dist-info/RECORD +++ /dev/null @@ -1,64 +0,0 @@ -psutil-7.0.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -psutil-7.0.0.dist-info/LICENSE,sha256=x63E1dEzelSLlnQh8fviWLkwM6BBdwj9b044-Oy864A,1577 -psutil-7.0.0.dist-info/METADATA,sha256=jEGY38opff7gdO5GOUIH8xeWXCXcGvitOIYrlUeVp8E,23136 -psutil-7.0.0.dist-info/RECORD,, -psutil-7.0.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -psutil-7.0.0.dist-info/WHEEL,sha256=-EX5DQzNGQEoyL99Q-0P0-D-CXbfqafenaAeiSQ_Ufk,100 -psutil-7.0.0.dist-info/top_level.txt,sha256=gCNhn57wzksDjSAISmgMJ0aiXzQulk0GJhb2-BAyYgw,7 -psutil/__init__.py,sha256=lvZWdYQ3W0flcZeW1vbN5QgoSHuqofXBnMxHZLzwgrU,89075 -psutil/__pycache__/__init__.cpython-313.pyc,, -psutil/__pycache__/_common.cpython-313.pyc,, -psutil/__pycache__/_psaix.cpython-313.pyc,, -psutil/__pycache__/_psbsd.cpython-313.pyc,, -psutil/__pycache__/_pslinux.cpython-313.pyc,, -psutil/__pycache__/_psosx.cpython-313.pyc,, -psutil/__pycache__/_psposix.cpython-313.pyc,, -psutil/__pycache__/_pssunos.cpython-313.pyc,, -psutil/__pycache__/_pswindows.cpython-313.pyc,, -psutil/_common.py,sha256=tPE7YVzC0ZIBhZzYdzqOFnh-geJVALbyBY3TSAwASXw,29592 -psutil/_psaix.py,sha256=CFBLwUi8DR5KsDC0yCs0jlLtLf2dhhyGArAhG_udqK8,18817 -psutil/_psbsd.py,sha256=UXd-QXUVk_H_wbFHWt2vshcChWxBrPwn38PX0HeYXfo,32727 -psutil/_pslinux.py,sha256=wKT1c3HX8XhnZ8sDNX1hiKRbVj7p53ASJ6VaniKaxs4,88323 -psutil/_psosx.py,sha256=LwFP6AtKp2hzNWRSaSLaWHB6nh1CiKSMu_KvP5009IE,16421 -psutil/_psposix.py,sha256=AJxyaRPf1h8dyT9rnsF8c-psHwXEbKqaNEt3OOm4Zuk,7349 -psutil/_pssunos.py,sha256=B58FY4JjbfndrdmbEV7QGX6lVi0v--V-g_Hxsg958MM,25654 -psutil/_psutil_windows.pyd,sha256=nH_IVdnRYU5wcFx9zG9Kw83Kta3-tqZ9OC9a3gnq3BU,67072 -psutil/_pswindows.py,sha256=is_Cq3yMuFnqGUfpOeiU8oWzZQNWmK_-Xt7asb8YCu4,37052 -psutil/tests/__init__.py,sha256=tFfa1RqnPJP9UuVe-JN7sAavHVoHlmqiN0pyk3I4KI0,66129 -psutil/tests/__main__.py,sha256=AQDwErrSFPsBGSY5wIKmh7LziqWTAARYKEqz_zrXMTc,321 -psutil/tests/__pycache__/__init__.cpython-313.pyc,, -psutil/tests/__pycache__/__main__.cpython-313.pyc,, -psutil/tests/__pycache__/test_aix.cpython-313.pyc,, -psutil/tests/__pycache__/test_bsd.cpython-313.pyc,, -psutil/tests/__pycache__/test_connections.cpython-313.pyc,, -psutil/tests/__pycache__/test_contracts.cpython-313.pyc,, -psutil/tests/__pycache__/test_linux.cpython-313.pyc,, -psutil/tests/__pycache__/test_memleaks.cpython-313.pyc,, -psutil/tests/__pycache__/test_misc.cpython-313.pyc,, -psutil/tests/__pycache__/test_osx.cpython-313.pyc,, -psutil/tests/__pycache__/test_posix.cpython-313.pyc,, -psutil/tests/__pycache__/test_process.cpython-313.pyc,, -psutil/tests/__pycache__/test_process_all.cpython-313.pyc,, -psutil/tests/__pycache__/test_scripts.cpython-313.pyc,, -psutil/tests/__pycache__/test_sunos.cpython-313.pyc,, -psutil/tests/__pycache__/test_system.cpython-313.pyc,, -psutil/tests/__pycache__/test_testutils.cpython-313.pyc,, -psutil/tests/__pycache__/test_unicode.cpython-313.pyc,, -psutil/tests/__pycache__/test_windows.cpython-313.pyc,, -psutil/tests/test_aix.py,sha256=M84ZfM1EeSDRyzrf404JGu5zy_ErRn5MK3t3yT11lz0,4550 -psutil/tests/test_bsd.py,sha256=GRbzguegV7K2m-O4dQJlUJGh7M7UH9fI8jvE2UjFOks,20784 -psutil/tests/test_connections.py,sha256=1D4HEQl_bZfZbk_4g-hc4rnrDqUD7q42rb9aIoJ1Amc,21723 -psutil/tests/test_contracts.py,sha256=8enorS1KmOftKyC4XTINXtnWNnmh18qFEYdLHKtrknY,12326 -psutil/tests/test_linux.py,sha256=Gap-GA6Bv9TfKbRupph49NwZC0vjv1V5isa5l-Eg3VY,91187 -psutil/tests/test_memleaks.py,sha256=yGEhTOOllW2NV-R2S5lybD2S8iO9CiS7--ND2E9rZTY,15608 -psutil/tests/test_misc.py,sha256=eQUmQqnh7nM0aIND_yOtgO4-Lwyspw3tgt3aWje0BPY,30545 -psutil/tests/test_osx.py,sha256=Y_NubjMylA88A7WAepiDkYTZl4YWvwgtCRFvu23Rm8A,6512 -psutil/tests/test_posix.py,sha256=yKL9N6ixiFUPNmv5v7cBS1OaoOZl-bm7hGdf7m2_M0E,17675 -psutil/tests/test_process.py,sha256=lmVD9FO6GJp4GZqSxR0j4Wsr7dcG8Py2qWsiv-furbY,61548 -psutil/tests/test_process_all.py,sha256=MfKc2BpL3pFBKqdmkq5gJ1GDD4CZzxUvesQ7sgnzzJg,18882 -psutil/tests/test_scripts.py,sha256=a3z4vFdBNlto_2pKe37kOPK6zVenfWzCsibduzyeO1c,7965 -psutil/tests/test_sunos.py,sha256=FxIAhIC3hycvJhgdVK8_98AmmV1pTZgXtovBgDmD9RA,1229 -psutil/tests/test_system.py,sha256=gi5Mci_pL5za6Q1Wp3nOv4J6Bj7L-IeM76rR5-1rFZk,37086 -psutil/tests/test_testutils.py,sha256=IUc3mMHGKGdnPpf_mhMWbDq0UDK9sXI4DZZ2dwujS7A,18915 -psutil/tests/test_unicode.py,sha256=kH08SGFmi0bxnL-LHEA7JX0fwauGPvY9W3zfkQumgTw,10705 -psutil/tests/test_windows.py,sha256=Zpg4Ek9KHkloYHpgHtHIrYBDXIgSf8HppSWcgWgogWE,34128 diff --git a/PortablePython/Lib/site-packages/psutil-7.0.0.dist-info/REQUESTED b/PortablePython/Lib/site-packages/psutil-7.0.0.dist-info/REQUESTED deleted file mode 100644 index e69de29..0000000 diff --git a/PortablePython/Lib/site-packages/psutil-7.0.0.dist-info/WHEEL b/PortablePython/Lib/site-packages/psutil-7.0.0.dist-info/WHEEL deleted file mode 100644 index 816dfea..0000000 --- a/PortablePython/Lib/site-packages/psutil-7.0.0.dist-info/WHEEL +++ /dev/null @@ -1,5 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.42.0) -Root-Is-Purelib: false -Tag: cp37-abi3-win_amd64 - diff --git a/PortablePython/Lib/site-packages/psutil-7.0.0.dist-info/top_level.txt b/PortablePython/Lib/site-packages/psutil-7.0.0.dist-info/top_level.txt deleted file mode 100644 index a4d92cc..0000000 --- a/PortablePython/Lib/site-packages/psutil-7.0.0.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -psutil diff --git a/PortablePython/Lib/site-packages/psutil/__init__.py b/PortablePython/Lib/site-packages/psutil/__init__.py deleted file mode 100644 index cf4a580..0000000 --- a/PortablePython/Lib/site-packages/psutil/__init__.py +++ /dev/null @@ -1,2407 +0,0 @@ -# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -"""psutil is a cross-platform library for retrieving information on -running processes and system utilization (CPU, memory, disks, network, -sensors) in Python. Supported platforms: - - - Linux - - Windows - - macOS - - FreeBSD - - OpenBSD - - NetBSD - - Sun Solaris - - AIX - -Supported Python versions are cPython 3.6+ and PyPy. -""" - -import collections -import contextlib -import datetime -import functools -import os -import signal -import socket -import subprocess -import sys -import threading -import time - - -try: - import pwd -except ImportError: - pwd = None - -from . import _common -from ._common import AIX -from ._common import BSD -from ._common import CONN_CLOSE -from ._common import CONN_CLOSE_WAIT -from ._common import CONN_CLOSING -from ._common import CONN_ESTABLISHED -from ._common import CONN_FIN_WAIT1 -from ._common import CONN_FIN_WAIT2 -from ._common import CONN_LAST_ACK -from ._common import CONN_LISTEN -from ._common import CONN_NONE -from ._common import CONN_SYN_RECV -from ._common import CONN_SYN_SENT -from ._common import CONN_TIME_WAIT -from ._common import FREEBSD -from ._common import LINUX -from ._common import MACOS -from ._common import NETBSD -from ._common import NIC_DUPLEX_FULL -from ._common import NIC_DUPLEX_HALF -from ._common import NIC_DUPLEX_UNKNOWN -from ._common import OPENBSD -from ._common import OSX # deprecated alias -from ._common import POSIX -from ._common import POWER_TIME_UNKNOWN -from ._common import POWER_TIME_UNLIMITED -from ._common import STATUS_DEAD -from ._common import STATUS_DISK_SLEEP -from ._common import STATUS_IDLE -from ._common import STATUS_LOCKED -from ._common import STATUS_PARKED -from ._common import STATUS_RUNNING -from ._common import STATUS_SLEEPING -from ._common import STATUS_STOPPED -from ._common import STATUS_TRACING_STOP -from ._common import STATUS_WAITING -from ._common import STATUS_WAKING -from ._common import STATUS_ZOMBIE -from ._common import SUNOS -from ._common import WINDOWS -from ._common import AccessDenied -from ._common import Error -from ._common import NoSuchProcess -from ._common import TimeoutExpired -from ._common import ZombieProcess -from ._common import debug -from ._common import memoize_when_activated -from ._common import wrap_numbers as _wrap_numbers - - -if LINUX: - # This is public API and it will be retrieved from _pslinux.py - # via sys.modules. - PROCFS_PATH = "/proc" - - from . import _pslinux as _psplatform - from ._pslinux import IOPRIO_CLASS_BE # noqa: F401 - from ._pslinux import IOPRIO_CLASS_IDLE # noqa: F401 - from ._pslinux import IOPRIO_CLASS_NONE # noqa: F401 - from ._pslinux import IOPRIO_CLASS_RT # noqa: F401 - -elif WINDOWS: - from . import _pswindows as _psplatform - from ._psutil_windows import ABOVE_NORMAL_PRIORITY_CLASS # noqa: F401 - from ._psutil_windows import BELOW_NORMAL_PRIORITY_CLASS # noqa: F401 - from ._psutil_windows import HIGH_PRIORITY_CLASS # noqa: F401 - from ._psutil_windows import IDLE_PRIORITY_CLASS # noqa: F401 - from ._psutil_windows import NORMAL_PRIORITY_CLASS # noqa: F401 - from ._psutil_windows import REALTIME_PRIORITY_CLASS # noqa: F401 - from ._pswindows import CONN_DELETE_TCB # noqa: F401 - from ._pswindows import IOPRIO_HIGH # noqa: F401 - from ._pswindows import IOPRIO_LOW # noqa: F401 - from ._pswindows import IOPRIO_NORMAL # noqa: F401 - from ._pswindows import IOPRIO_VERYLOW # noqa: F401 - -elif MACOS: - from . import _psosx as _psplatform - -elif BSD: - from . import _psbsd as _psplatform - -elif SUNOS: - from . import _pssunos as _psplatform - from ._pssunos import CONN_BOUND # noqa: F401 - from ._pssunos import CONN_IDLE # noqa: F401 - - # This is public writable API which is read from _pslinux.py and - # _pssunos.py via sys.modules. - PROCFS_PATH = "/proc" - -elif AIX: - from . import _psaix as _psplatform - - # This is public API and it will be retrieved from _pslinux.py - # via sys.modules. - PROCFS_PATH = "/proc" - -else: # pragma: no cover - msg = f"platform {sys.platform} is not supported" - raise NotImplementedError(msg) - - -# fmt: off -__all__ = [ - # exceptions - "Error", "NoSuchProcess", "ZombieProcess", "AccessDenied", - "TimeoutExpired", - - # constants - "version_info", "__version__", - - "STATUS_RUNNING", "STATUS_IDLE", "STATUS_SLEEPING", "STATUS_DISK_SLEEP", - "STATUS_STOPPED", "STATUS_TRACING_STOP", "STATUS_ZOMBIE", "STATUS_DEAD", - "STATUS_WAKING", "STATUS_LOCKED", "STATUS_WAITING", "STATUS_LOCKED", - "STATUS_PARKED", - - "CONN_ESTABLISHED", "CONN_SYN_SENT", "CONN_SYN_RECV", "CONN_FIN_WAIT1", - "CONN_FIN_WAIT2", "CONN_TIME_WAIT", "CONN_CLOSE", "CONN_CLOSE_WAIT", - "CONN_LAST_ACK", "CONN_LISTEN", "CONN_CLOSING", "CONN_NONE", - # "CONN_IDLE", "CONN_BOUND", - - "AF_LINK", - - "NIC_DUPLEX_FULL", "NIC_DUPLEX_HALF", "NIC_DUPLEX_UNKNOWN", - - "POWER_TIME_UNKNOWN", "POWER_TIME_UNLIMITED", - - "BSD", "FREEBSD", "LINUX", "NETBSD", "OPENBSD", "MACOS", "OSX", "POSIX", - "SUNOS", "WINDOWS", "AIX", - - # "RLIM_INFINITY", "RLIMIT_AS", "RLIMIT_CORE", "RLIMIT_CPU", "RLIMIT_DATA", - # "RLIMIT_FSIZE", "RLIMIT_LOCKS", "RLIMIT_MEMLOCK", "RLIMIT_NOFILE", - # "RLIMIT_NPROC", "RLIMIT_RSS", "RLIMIT_STACK", "RLIMIT_MSGQUEUE", - # "RLIMIT_NICE", "RLIMIT_RTPRIO", "RLIMIT_RTTIME", "RLIMIT_SIGPENDING", - - # classes - "Process", "Popen", - - # functions - "pid_exists", "pids", "process_iter", "wait_procs", # proc - "virtual_memory", "swap_memory", # memory - "cpu_times", "cpu_percent", "cpu_times_percent", "cpu_count", # cpu - "cpu_stats", # "cpu_freq", "getloadavg" - "net_io_counters", "net_connections", "net_if_addrs", # network - "net_if_stats", - "disk_io_counters", "disk_partitions", "disk_usage", # disk - # "sensors_temperatures", "sensors_battery", "sensors_fans" # sensors - "users", "boot_time", # others -] -# fmt: on - - -__all__.extend(_psplatform.__extra__all__) - -# Linux, FreeBSD -if hasattr(_psplatform.Process, "rlimit"): - # Populate global namespace with RLIM* constants. - from . import _psutil_posix - - _globals = globals() - _name = None - for _name in dir(_psutil_posix): - if _name.startswith('RLIM') and _name.isupper(): - _globals[_name] = getattr(_psutil_posix, _name) - __all__.append(_name) - del _globals, _name - -AF_LINK = _psplatform.AF_LINK - -__author__ = "Giampaolo Rodola'" -__version__ = "7.0.0" -version_info = tuple(int(num) for num in __version__.split('.')) - -_timer = getattr(time, 'monotonic', time.time) -_TOTAL_PHYMEM = None -_LOWEST_PID = None -_SENTINEL = object() - -# Sanity check in case the user messed up with psutil installation -# or did something weird with sys.path. In this case we might end -# up importing a python module using a C extension module which -# was compiled for a different version of psutil. -# We want to prevent that by failing sooner rather than later. -# See: https://github.com/giampaolo/psutil/issues/564 -if int(__version__.replace('.', '')) != getattr( - _psplatform.cext, 'version', None -): - msg = f"version conflict: {_psplatform.cext.__file__!r} C extension " - msg += "module was built for another version of psutil" - if hasattr(_psplatform.cext, 'version'): - v = ".".join(list(str(_psplatform.cext.version))) - msg += f" ({v} instead of {__version__})" - else: - msg += f" (different than {__version__})" - what = getattr( - _psplatform.cext, - "__file__", - "the existing psutil install directory", - ) - msg += f"; you may try to 'pip uninstall psutil', manually remove {what}" - msg += " or clean the virtual env somehow, then reinstall" - raise ImportError(msg) - - -# ===================================================================== -# --- Utils -# ===================================================================== - - -if hasattr(_psplatform, 'ppid_map'): - # Faster version (Windows and Linux). - _ppid_map = _psplatform.ppid_map -else: # pragma: no cover - - def _ppid_map(): - """Return a {pid: ppid, ...} dict for all running processes in - one shot. Used to speed up Process.children(). - """ - ret = {} - for pid in pids(): - try: - ret[pid] = _psplatform.Process(pid).ppid() - except (NoSuchProcess, ZombieProcess): - pass - return ret - - -def _pprint_secs(secs): - """Format seconds in a human readable form.""" - now = time.time() - secs_ago = int(now - secs) - fmt = "%H:%M:%S" if secs_ago < 60 * 60 * 24 else "%Y-%m-%d %H:%M:%S" - return datetime.datetime.fromtimestamp(secs).strftime(fmt) - - -def _check_conn_kind(kind): - """Check net_connections()'s `kind` parameter.""" - kinds = tuple(_common.conn_tmap) - if kind not in kinds: - msg = f"invalid kind argument {kind!r}; valid ones are: {kinds}" - raise ValueError(msg) - - -# ===================================================================== -# --- Process class -# ===================================================================== - - -class Process: - """Represents an OS process with the given PID. - If PID is omitted current process PID (os.getpid()) is used. - Raise NoSuchProcess if PID does not exist. - - Note that most of the methods of this class do not make sure that - the PID of the process being queried has been reused. That means - that you may end up retrieving information for another process. - - The only exceptions for which process identity is pre-emptively - checked and guaranteed are: - - - parent() - - children() - - nice() (set) - - ionice() (set) - - rlimit() (set) - - cpu_affinity (set) - - suspend() - - resume() - - send_signal() - - terminate() - - kill() - - To prevent this problem for all other methods you can use - is_running() before querying the process. - """ - - def __init__(self, pid=None): - self._init(pid) - - def _init(self, pid, _ignore_nsp=False): - if pid is None: - pid = os.getpid() - else: - if pid < 0: - msg = f"pid must be a positive integer (got {pid})" - raise ValueError(msg) - try: - _psplatform.cext.check_pid_range(pid) - except OverflowError as err: - msg = "process PID out of range" - raise NoSuchProcess(pid, msg=msg) from err - - self._pid = pid - self._name = None - self._exe = None - self._create_time = None - self._gone = False - self._pid_reused = False - self._hash = None - self._lock = threading.RLock() - # used for caching on Windows only (on POSIX ppid may change) - self._ppid = None - # platform-specific modules define an _psplatform.Process - # implementation class - self._proc = _psplatform.Process(pid) - self._last_sys_cpu_times = None - self._last_proc_cpu_times = None - self._exitcode = _SENTINEL - self._ident = (self.pid, None) - try: - self._ident = self._get_ident() - except AccessDenied: - # This should happen on Windows only, since we use the fast - # create time method. AFAIK, on all other platforms we are - # able to get create time for all PIDs. - pass - except ZombieProcess: - # Zombies can still be queried by this class (although - # not always) and pids() return them so just go on. - pass - except NoSuchProcess: - if not _ignore_nsp: - msg = "process PID not found" - raise NoSuchProcess(pid, msg=msg) from None - self._gone = True - - def _get_ident(self): - """Return a (pid, uid) tuple which is supposed to identify a - Process instance univocally over time. The PID alone is not - enough, as it can be assigned to a new process after this one - terminates, so we add process creation time to the mix. We need - this in order to prevent killing the wrong process later on. - This is also known as PID reuse or PID recycling problem. - - The reliability of this strategy mostly depends on - create_time() precision, which is 0.01 secs on Linux. The - assumption is that, after a process terminates, the kernel - won't reuse the same PID after such a short period of time - (0.01 secs). Technically this is inherently racy, but - practically it should be good enough. - """ - if WINDOWS: - # Use create_time() fast method in order to speedup - # `process_iter()`. This means we'll get AccessDenied for - # most ADMIN processes, but that's fine since it means - # we'll also get AccessDenied on kill(). - # https://github.com/giampaolo/psutil/issues/2366#issuecomment-2381646555 - self._create_time = self._proc.create_time(fast_only=True) - return (self.pid, self._create_time) - else: - return (self.pid, self.create_time()) - - def __str__(self): - info = collections.OrderedDict() - info["pid"] = self.pid - if self._name: - info['name'] = self._name - with self.oneshot(): - if self._pid_reused: - info["status"] = "terminated + PID reused" - else: - try: - info["name"] = self.name() - info["status"] = self.status() - except ZombieProcess: - info["status"] = "zombie" - except NoSuchProcess: - info["status"] = "terminated" - except AccessDenied: - pass - - if self._exitcode not in {_SENTINEL, None}: - info["exitcode"] = self._exitcode - if self._create_time is not None: - info['started'] = _pprint_secs(self._create_time) - - return "{}.{}({})".format( - self.__class__.__module__, - self.__class__.__name__, - ", ".join([f"{k}={v!r}" for k, v in info.items()]), - ) - - __repr__ = __str__ - - def __eq__(self, other): - # Test for equality with another Process object based - # on PID and creation time. - if not isinstance(other, Process): - return NotImplemented - if OPENBSD or NETBSD: # pragma: no cover - # Zombie processes on Open/NetBSD have a creation time of - # 0.0. This covers the case when a process started normally - # (so it has a ctime), then it turned into a zombie. It's - # important to do this because is_running() depends on - # __eq__. - pid1, ident1 = self._ident - pid2, ident2 = other._ident - if pid1 == pid2: - if ident1 and not ident2: - try: - return self.status() == STATUS_ZOMBIE - except Error: - pass - return self._ident == other._ident - - def __ne__(self, other): - return not self == other - - def __hash__(self): - if self._hash is None: - self._hash = hash(self._ident) - return self._hash - - def _raise_if_pid_reused(self): - """Raises NoSuchProcess in case process PID has been reused.""" - if self._pid_reused or (not self.is_running() and self._pid_reused): - # We may directly raise NSP in here already if PID is just - # not running, but I prefer NSP to be raised naturally by - # the actual Process API call. This way unit tests will tell - # us if the API is broken (aka don't raise NSP when it - # should). We also remain consistent with all other "get" - # APIs which don't use _raise_if_pid_reused(). - msg = "process no longer exists and its PID has been reused" - raise NoSuchProcess(self.pid, self._name, msg=msg) - - @property - def pid(self): - """The process PID.""" - return self._pid - - # --- utility methods - - @contextlib.contextmanager - def oneshot(self): - """Utility context manager which considerably speeds up the - retrieval of multiple process information at the same time. - - Internally different process info (e.g. name, ppid, uids, - gids, ...) may be fetched by using the same routine, but - only one information is returned and the others are discarded. - When using this context manager the internal routine is - executed once (in the example below on name()) and the - other info are cached. - - The cache is cleared when exiting the context manager block. - The advice is to use this every time you retrieve more than - one information about the process. If you're lucky, you'll - get a hell of a speedup. - - >>> import psutil - >>> p = psutil.Process() - >>> with p.oneshot(): - ... p.name() # collect multiple info - ... p.cpu_times() # return cached value - ... p.cpu_percent() # return cached value - ... p.create_time() # return cached value - ... - >>> - """ - with self._lock: - if hasattr(self, "_cache"): - # NOOP: this covers the use case where the user enters the - # context twice: - # - # >>> with p.oneshot(): - # ... with p.oneshot(): - # ... - # - # Also, since as_dict() internally uses oneshot() - # I expect that the code below will be a pretty common - # "mistake" that the user will make, so let's guard - # against that: - # - # >>> with p.oneshot(): - # ... p.as_dict() - # ... - yield - else: - try: - # cached in case cpu_percent() is used - self.cpu_times.cache_activate(self) - # cached in case memory_percent() is used - self.memory_info.cache_activate(self) - # cached in case parent() is used - self.ppid.cache_activate(self) - # cached in case username() is used - if POSIX: - self.uids.cache_activate(self) - # specific implementation cache - self._proc.oneshot_enter() - yield - finally: - self.cpu_times.cache_deactivate(self) - self.memory_info.cache_deactivate(self) - self.ppid.cache_deactivate(self) - if POSIX: - self.uids.cache_deactivate(self) - self._proc.oneshot_exit() - - def as_dict(self, attrs=None, ad_value=None): - """Utility method returning process information as a - hashable dictionary. - If *attrs* is specified it must be a list of strings - reflecting available Process class' attribute names - (e.g. ['cpu_times', 'name']) else all public (read - only) attributes are assumed. - *ad_value* is the value which gets assigned in case - AccessDenied or ZombieProcess exception is raised when - retrieving that particular process information. - """ - valid_names = _as_dict_attrnames - if attrs is not None: - if not isinstance(attrs, (list, tuple, set, frozenset)): - msg = f"invalid attrs type {type(attrs)}" - raise TypeError(msg) - attrs = set(attrs) - invalid_names = attrs - valid_names - if invalid_names: - msg = "invalid attr name{} {}".format( - "s" if len(invalid_names) > 1 else "", - ", ".join(map(repr, invalid_names)), - ) - raise ValueError(msg) - - retdict = {} - ls = attrs or valid_names - with self.oneshot(): - for name in ls: - try: - if name == 'pid': - ret = self.pid - else: - meth = getattr(self, name) - ret = meth() - except (AccessDenied, ZombieProcess): - ret = ad_value - except NotImplementedError: - # in case of not implemented functionality (may happen - # on old or exotic systems) we want to crash only if - # the user explicitly asked for that particular attr - if attrs: - raise - continue - retdict[name] = ret - return retdict - - def parent(self): - """Return the parent process as a Process object pre-emptively - checking whether PID has been reused. - If no parent is known return None. - """ - lowest_pid = _LOWEST_PID if _LOWEST_PID is not None else pids()[0] - if self.pid == lowest_pid: - return None - ppid = self.ppid() - if ppid is not None: - ctime = self.create_time() - try: - parent = Process(ppid) - if parent.create_time() <= ctime: - return parent - # ...else ppid has been reused by another process - except NoSuchProcess: - pass - - def parents(self): - """Return the parents of this process as a list of Process - instances. If no parents are known return an empty list. - """ - parents = [] - proc = self.parent() - while proc is not None: - parents.append(proc) - proc = proc.parent() - return parents - - def is_running(self): - """Return whether this process is running. - - It also checks if PID has been reused by another process, in - which case it will remove the process from `process_iter()` - internal cache and return False. - """ - if self._gone or self._pid_reused: - return False - try: - # Checking if PID is alive is not enough as the PID might - # have been reused by another process. Process identity / - # uniqueness over time is guaranteed by (PID + creation - # time) and that is verified in __eq__. - self._pid_reused = self != Process(self.pid) - if self._pid_reused: - _pids_reused.add(self.pid) - raise NoSuchProcess(self.pid) - return True - except ZombieProcess: - # We should never get here as it's already handled in - # Process.__init__; here just for extra safety. - return True - except NoSuchProcess: - self._gone = True - return False - - # --- actual API - - @memoize_when_activated - def ppid(self): - """The process parent PID. - On Windows the return value is cached after first call. - """ - # On POSIX we don't want to cache the ppid as it may unexpectedly - # change to 1 (init) in case this process turns into a zombie: - # https://github.com/giampaolo/psutil/issues/321 - # http://stackoverflow.com/questions/356722/ - - # XXX should we check creation time here rather than in - # Process.parent()? - self._raise_if_pid_reused() - if POSIX: - return self._proc.ppid() - else: # pragma: no cover - self._ppid = self._ppid or self._proc.ppid() - return self._ppid - - def name(self): - """The process name. The return value is cached after first call.""" - # Process name is only cached on Windows as on POSIX it may - # change, see: - # https://github.com/giampaolo/psutil/issues/692 - if WINDOWS and self._name is not None: - return self._name - name = self._proc.name() - if POSIX and len(name) >= 15: - # On UNIX the name gets truncated to the first 15 characters. - # If it matches the first part of the cmdline we return that - # one instead because it's usually more explicative. - # Examples are "gnome-keyring-d" vs. "gnome-keyring-daemon". - try: - cmdline = self.cmdline() - except (AccessDenied, ZombieProcess): - # Just pass and return the truncated name: it's better - # than nothing. Note: there are actual cases where a - # zombie process can return a name() but not a - # cmdline(), see: - # https://github.com/giampaolo/psutil/issues/2239 - pass - else: - if cmdline: - extended_name = os.path.basename(cmdline[0]) - if extended_name.startswith(name): - name = extended_name - self._name = name - self._proc._name = name - return name - - def exe(self): - """The process executable as an absolute path. - May also be an empty string. - The return value is cached after first call. - """ - - def guess_it(fallback): - # try to guess exe from cmdline[0] in absence of a native - # exe representation - cmdline = self.cmdline() - if cmdline and hasattr(os, 'access') and hasattr(os, 'X_OK'): - exe = cmdline[0] # the possible exe - # Attempt to guess only in case of an absolute path. - # It is not safe otherwise as the process might have - # changed cwd. - if ( - os.path.isabs(exe) - and os.path.isfile(exe) - and os.access(exe, os.X_OK) - ): - return exe - if isinstance(fallback, AccessDenied): - raise fallback - return fallback - - if self._exe is None: - try: - exe = self._proc.exe() - except AccessDenied as err: - return guess_it(fallback=err) - else: - if not exe: - # underlying implementation can legitimately return an - # empty string; if that's the case we don't want to - # raise AD while guessing from the cmdline - try: - exe = guess_it(fallback=exe) - except AccessDenied: - pass - self._exe = exe - return self._exe - - def cmdline(self): - """The command line this process has been called with.""" - return self._proc.cmdline() - - def status(self): - """The process current status as a STATUS_* constant.""" - try: - return self._proc.status() - except ZombieProcess: - return STATUS_ZOMBIE - - def username(self): - """The name of the user that owns the process. - On UNIX this is calculated by using *real* process uid. - """ - if POSIX: - if pwd is None: - # might happen if python was installed from sources - msg = "requires pwd module shipped with standard python" - raise ImportError(msg) - real_uid = self.uids().real - try: - return pwd.getpwuid(real_uid).pw_name - except KeyError: - # the uid can't be resolved by the system - return str(real_uid) - else: - return self._proc.username() - - def create_time(self): - """The process creation time as a floating point number - expressed in seconds since the epoch. - The return value is cached after first call. - """ - if self._create_time is None: - self._create_time = self._proc.create_time() - return self._create_time - - def cwd(self): - """Process current working directory as an absolute path.""" - return self._proc.cwd() - - def nice(self, value=None): - """Get or set process niceness (priority).""" - if value is None: - return self._proc.nice_get() - else: - self._raise_if_pid_reused() - self._proc.nice_set(value) - - if POSIX: - - @memoize_when_activated - def uids(self): - """Return process UIDs as a (real, effective, saved) - namedtuple. - """ - return self._proc.uids() - - def gids(self): - """Return process GIDs as a (real, effective, saved) - namedtuple. - """ - return self._proc.gids() - - def terminal(self): - """The terminal associated with this process, if any, - else None. - """ - return self._proc.terminal() - - def num_fds(self): - """Return the number of file descriptors opened by this - process (POSIX only). - """ - return self._proc.num_fds() - - # Linux, BSD, AIX and Windows only - if hasattr(_psplatform.Process, "io_counters"): - - def io_counters(self): - """Return process I/O statistics as a - (read_count, write_count, read_bytes, write_bytes) - namedtuple. - Those are the number of read/write calls performed and the - amount of bytes read and written by the process. - """ - return self._proc.io_counters() - - # Linux and Windows - if hasattr(_psplatform.Process, "ionice_get"): - - def ionice(self, ioclass=None, value=None): - """Get or set process I/O niceness (priority). - - On Linux *ioclass* is one of the IOPRIO_CLASS_* constants. - *value* is a number which goes from 0 to 7. The higher the - value, the lower the I/O priority of the process. - - On Windows only *ioclass* is used and it can be set to 2 - (normal), 1 (low) or 0 (very low). - - Available on Linux and Windows > Vista only. - """ - if ioclass is None: - if value is not None: - msg = "'ioclass' argument must be specified" - raise ValueError(msg) - return self._proc.ionice_get() - else: - self._raise_if_pid_reused() - return self._proc.ionice_set(ioclass, value) - - # Linux / FreeBSD only - if hasattr(_psplatform.Process, "rlimit"): - - def rlimit(self, resource, limits=None): - """Get or set process resource limits as a (soft, hard) - tuple. - - *resource* is one of the RLIMIT_* constants. - *limits* is supposed to be a (soft, hard) tuple. - - See "man prlimit" for further info. - Available on Linux and FreeBSD only. - """ - if limits is not None: - self._raise_if_pid_reused() - return self._proc.rlimit(resource, limits) - - # Windows, Linux and FreeBSD only - if hasattr(_psplatform.Process, "cpu_affinity_get"): - - def cpu_affinity(self, cpus=None): - """Get or set process CPU affinity. - If specified, *cpus* must be a list of CPUs for which you - want to set the affinity (e.g. [0, 1]). - If an empty list is passed, all egible CPUs are assumed - (and set). - (Windows, Linux and BSD only). - """ - if cpus is None: - return sorted(set(self._proc.cpu_affinity_get())) - else: - self._raise_if_pid_reused() - if not cpus: - if hasattr(self._proc, "_get_eligible_cpus"): - cpus = self._proc._get_eligible_cpus() - else: - cpus = tuple(range(len(cpu_times(percpu=True)))) - self._proc.cpu_affinity_set(list(set(cpus))) - - # Linux, FreeBSD, SunOS - if hasattr(_psplatform.Process, "cpu_num"): - - def cpu_num(self): - """Return what CPU this process is currently running on. - The returned number should be <= psutil.cpu_count() - and <= len(psutil.cpu_percent(percpu=True)). - It may be used in conjunction with - psutil.cpu_percent(percpu=True) to observe the system - workload distributed across CPUs. - """ - return self._proc.cpu_num() - - # All platforms has it, but maybe not in the future. - if hasattr(_psplatform.Process, "environ"): - - def environ(self): - """The environment variables of the process as a dict. Note: this - might not reflect changes made after the process started. - """ - return self._proc.environ() - - if WINDOWS: - - def num_handles(self): - """Return the number of handles opened by this process - (Windows only). - """ - return self._proc.num_handles() - - def num_ctx_switches(self): - """Return the number of voluntary and involuntary context - switches performed by this process. - """ - return self._proc.num_ctx_switches() - - def num_threads(self): - """Return the number of threads used by this process.""" - return self._proc.num_threads() - - if hasattr(_psplatform.Process, "threads"): - - def threads(self): - """Return threads opened by process as a list of - (id, user_time, system_time) namedtuples representing - thread id and thread CPU times (user/system). - On OpenBSD this method requires root access. - """ - return self._proc.threads() - - def children(self, recursive=False): - """Return the children of this process as a list of Process - instances, pre-emptively checking whether PID has been reused. - If *recursive* is True return all the parent descendants. - - Example (A == this process): - - A ─┐ - │ - ├─ B (child) ─┐ - │ └─ X (grandchild) ─┐ - │ └─ Y (great grandchild) - ├─ C (child) - └─ D (child) - - >>> import psutil - >>> p = psutil.Process() - >>> p.children() - B, C, D - >>> p.children(recursive=True) - B, X, Y, C, D - - Note that in the example above if process X disappears - process Y won't be listed as the reference to process A - is lost. - """ - self._raise_if_pid_reused() - ppid_map = _ppid_map() - ret = [] - if not recursive: - for pid, ppid in ppid_map.items(): - if ppid == self.pid: - try: - child = Process(pid) - # if child happens to be older than its parent - # (self) it means child's PID has been reused - if self.create_time() <= child.create_time(): - ret.append(child) - except (NoSuchProcess, ZombieProcess): - pass - else: - # Construct a {pid: [child pids]} dict - reverse_ppid_map = collections.defaultdict(list) - for pid, ppid in ppid_map.items(): - reverse_ppid_map[ppid].append(pid) - # Recursively traverse that dict, starting from self.pid, - # such that we only call Process() on actual children - seen = set() - stack = [self.pid] - while stack: - pid = stack.pop() - if pid in seen: - # Since pids can be reused while the ppid_map is - # constructed, there may be rare instances where - # there's a cycle in the recorded process "tree". - continue - seen.add(pid) - for child_pid in reverse_ppid_map[pid]: - try: - child = Process(child_pid) - # if child happens to be older than its parent - # (self) it means child's PID has been reused - intime = self.create_time() <= child.create_time() - if intime: - ret.append(child) - stack.append(child_pid) - except (NoSuchProcess, ZombieProcess): - pass - return ret - - def cpu_percent(self, interval=None): - """Return a float representing the current process CPU - utilization as a percentage. - - When *interval* is 0.0 or None (default) compares process times - to system CPU times elapsed since last call, returning - immediately (non-blocking). That means that the first time - this is called it will return a meaningful 0.0 value. - - When *interval* is > 0.0 compares process times to system CPU - times elapsed before and after the interval (blocking). - - In this case is recommended for accuracy that this function - be called with at least 0.1 seconds between calls. - - A value > 100.0 can be returned in case of processes running - multiple threads on different CPU cores. - - The returned value is explicitly NOT split evenly between - all available logical CPUs. This means that a busy loop process - running on a system with 2 logical CPUs will be reported as - having 100% CPU utilization instead of 50%. - - Examples: - - >>> import psutil - >>> p = psutil.Process(os.getpid()) - >>> # blocking - >>> p.cpu_percent(interval=1) - 2.0 - >>> # non-blocking (percentage since last call) - >>> p.cpu_percent(interval=None) - 2.9 - >>> - """ - blocking = interval is not None and interval > 0.0 - if interval is not None and interval < 0: - msg = f"interval is not positive (got {interval!r})" - raise ValueError(msg) - num_cpus = cpu_count() or 1 - - def timer(): - return _timer() * num_cpus - - if blocking: - st1 = timer() - pt1 = self._proc.cpu_times() - time.sleep(interval) - st2 = timer() - pt2 = self._proc.cpu_times() - else: - st1 = self._last_sys_cpu_times - pt1 = self._last_proc_cpu_times - st2 = timer() - pt2 = self._proc.cpu_times() - if st1 is None or pt1 is None: - self._last_sys_cpu_times = st2 - self._last_proc_cpu_times = pt2 - return 0.0 - - delta_proc = (pt2.user - pt1.user) + (pt2.system - pt1.system) - delta_time = st2 - st1 - # reset values for next call in case of interval == None - self._last_sys_cpu_times = st2 - self._last_proc_cpu_times = pt2 - - try: - # This is the utilization split evenly between all CPUs. - # E.g. a busy loop process on a 2-CPU-cores system at this - # point is reported as 50% instead of 100%. - overall_cpus_percent = (delta_proc / delta_time) * 100 - except ZeroDivisionError: - # interval was too low - return 0.0 - else: - # Note 1: - # in order to emulate "top" we multiply the value for the num - # of CPU cores. This way the busy process will be reported as - # having 100% (or more) usage. - # - # Note 2: - # taskmgr.exe on Windows differs in that it will show 50% - # instead. - # - # Note 3: - # a percentage > 100 is legitimate as it can result from a - # process with multiple threads running on different CPU - # cores (top does the same), see: - # http://stackoverflow.com/questions/1032357 - # https://github.com/giampaolo/psutil/issues/474 - single_cpu_percent = overall_cpus_percent * num_cpus - return round(single_cpu_percent, 1) - - @memoize_when_activated - def cpu_times(self): - """Return a (user, system, children_user, children_system) - namedtuple representing the accumulated process time, in - seconds. - This is similar to os.times() but per-process. - On macOS and Windows children_user and children_system are - always set to 0. - """ - return self._proc.cpu_times() - - @memoize_when_activated - def memory_info(self): - """Return a namedtuple with variable fields depending on the - platform, representing memory information about the process. - - The "portable" fields available on all platforms are `rss` and `vms`. - - All numbers are expressed in bytes. - """ - return self._proc.memory_info() - - def memory_full_info(self): - """This method returns the same information as memory_info(), - plus, on some platform (Linux, macOS, Windows), also provides - additional metrics (USS, PSS and swap). - The additional metrics provide a better representation of actual - process memory usage. - - Namely USS is the memory which is unique to a process and which - would be freed if the process was terminated right now. - - It does so by passing through the whole process address. - As such it usually requires higher user privileges than - memory_info() and is considerably slower. - """ - return self._proc.memory_full_info() - - def memory_percent(self, memtype="rss"): - """Compare process memory to total physical system memory and - calculate process memory utilization as a percentage. - *memtype* argument is a string that dictates what type of - process memory you want to compare against (defaults to "rss"). - The list of available strings can be obtained like this: - - >>> psutil.Process().memory_info()._fields - ('rss', 'vms', 'shared', 'text', 'lib', 'data', 'dirty', 'uss', 'pss') - """ - valid_types = list(_psplatform.pfullmem._fields) - if memtype not in valid_types: - msg = ( - f"invalid memtype {memtype!r}; valid types are" - f" {tuple(valid_types)!r}" - ) - raise ValueError(msg) - fun = ( - self.memory_info - if memtype in _psplatform.pmem._fields - else self.memory_full_info - ) - metrics = fun() - value = getattr(metrics, memtype) - - # use cached value if available - total_phymem = _TOTAL_PHYMEM or virtual_memory().total - if not total_phymem > 0: - # we should never get here - msg = ( - "can't calculate process memory percent because total physical" - f" system memory is not positive ({total_phymem!r})" - ) - raise ValueError(msg) - return (value / float(total_phymem)) * 100 - - if hasattr(_psplatform.Process, "memory_maps"): - - def memory_maps(self, grouped=True): - """Return process' mapped memory regions as a list of namedtuples - whose fields are variable depending on the platform. - - If *grouped* is True the mapped regions with the same 'path' - are grouped together and the different memory fields are summed. - - If *grouped* is False every mapped region is shown as a single - entity and the namedtuple will also include the mapped region's - address space ('addr') and permission set ('perms'). - """ - it = self._proc.memory_maps() - if grouped: - d = {} - for tupl in it: - path = tupl[2] - nums = tupl[3:] - try: - d[path] = list(map(lambda x, y: x + y, d[path], nums)) - except KeyError: - d[path] = nums - nt = _psplatform.pmmap_grouped - return [nt(path, *d[path]) for path in d] - else: - nt = _psplatform.pmmap_ext - return [nt(*x) for x in it] - - def open_files(self): - """Return files opened by process as a list of - (path, fd) namedtuples including the absolute file name - and file descriptor number. - """ - return self._proc.open_files() - - def net_connections(self, kind='inet'): - """Return socket connections opened by process as a list of - (fd, family, type, laddr, raddr, status) namedtuples. - The *kind* parameter filters for connections that match the - following criteria: - - +------------+----------------------------------------------------+ - | Kind Value | Connections using | - +------------+----------------------------------------------------+ - | inet | IPv4 and IPv6 | - | inet4 | IPv4 | - | inet6 | IPv6 | - | tcp | TCP | - | tcp4 | TCP over IPv4 | - | tcp6 | TCP over IPv6 | - | udp | UDP | - | udp4 | UDP over IPv4 | - | udp6 | UDP over IPv6 | - | unix | UNIX socket (both UDP and TCP protocols) | - | all | the sum of all the possible families and protocols | - +------------+----------------------------------------------------+ - """ - _check_conn_kind(kind) - return self._proc.net_connections(kind) - - @_common.deprecated_method(replacement="net_connections") - def connections(self, kind="inet"): - return self.net_connections(kind=kind) - - # --- signals - - if POSIX: - - def _send_signal(self, sig): - assert not self.pid < 0, self.pid - self._raise_if_pid_reused() - - pid, ppid, name = self.pid, self._ppid, self._name - if pid == 0: - # see "man 2 kill" - msg = ( - "preventing sending signal to process with PID 0 as it " - "would affect every process in the process group of the " - "calling process (os.getpid()) instead of PID 0" - ) - raise ValueError(msg) - try: - os.kill(pid, sig) - except ProcessLookupError as err: - if OPENBSD and pid_exists(pid): - # We do this because os.kill() lies in case of - # zombie processes. - raise ZombieProcess(pid, name, ppid) from err - self._gone = True - raise NoSuchProcess(pid, name) from err - except PermissionError as err: - raise AccessDenied(pid, name) from err - - def send_signal(self, sig): - """Send a signal *sig* to process pre-emptively checking - whether PID has been reused (see signal module constants) . - On Windows only SIGTERM is valid and is treated as an alias - for kill(). - """ - if POSIX: - self._send_signal(sig) - else: # pragma: no cover - self._raise_if_pid_reused() - if sig != signal.SIGTERM and not self.is_running(): - msg = "process no longer exists" - raise NoSuchProcess(self.pid, self._name, msg=msg) - self._proc.send_signal(sig) - - def suspend(self): - """Suspend process execution with SIGSTOP pre-emptively checking - whether PID has been reused. - On Windows this has the effect of suspending all process threads. - """ - if POSIX: - self._send_signal(signal.SIGSTOP) - else: # pragma: no cover - self._raise_if_pid_reused() - self._proc.suspend() - - def resume(self): - """Resume process execution with SIGCONT pre-emptively checking - whether PID has been reused. - On Windows this has the effect of resuming all process threads. - """ - if POSIX: - self._send_signal(signal.SIGCONT) - else: # pragma: no cover - self._raise_if_pid_reused() - self._proc.resume() - - def terminate(self): - """Terminate the process with SIGTERM pre-emptively checking - whether PID has been reused. - On Windows this is an alias for kill(). - """ - if POSIX: - self._send_signal(signal.SIGTERM) - else: # pragma: no cover - self._raise_if_pid_reused() - self._proc.kill() - - def kill(self): - """Kill the current process with SIGKILL pre-emptively checking - whether PID has been reused. - """ - if POSIX: - self._send_signal(signal.SIGKILL) - else: # pragma: no cover - self._raise_if_pid_reused() - self._proc.kill() - - def wait(self, timeout=None): - """Wait for process to terminate and, if process is a children - of os.getpid(), also return its exit code, else None. - On Windows there's no such limitation (exit code is always - returned). - - If the process is already terminated immediately return None - instead of raising NoSuchProcess. - - If *timeout* (in seconds) is specified and process is still - alive raise TimeoutExpired. - - To wait for multiple Process(es) use psutil.wait_procs(). - """ - if timeout is not None and not timeout >= 0: - msg = "timeout must be a positive integer" - raise ValueError(msg) - if self._exitcode is not _SENTINEL: - return self._exitcode - self._exitcode = self._proc.wait(timeout) - return self._exitcode - - -# The valid attr names which can be processed by Process.as_dict(). -# fmt: off -_as_dict_attrnames = { - x for x in dir(Process) if not x.startswith("_") and x not in - {'send_signal', 'suspend', 'resume', 'terminate', 'kill', 'wait', - 'is_running', 'as_dict', 'parent', 'parents', 'children', 'rlimit', - 'connections', 'oneshot'} -} -# fmt: on - - -# ===================================================================== -# --- Popen class -# ===================================================================== - - -class Popen(Process): - """Same as subprocess.Popen, but in addition it provides all - psutil.Process methods in a single class. - For the following methods which are common to both classes, psutil - implementation takes precedence: - - * send_signal() - * terminate() - * kill() - - This is done in order to avoid killing another process in case its - PID has been reused, fixing BPO-6973. - - >>> import psutil - >>> from subprocess import PIPE - >>> p = psutil.Popen(["python", "-c", "print 'hi'"], stdout=PIPE) - >>> p.name() - 'python' - >>> p.uids() - user(real=1000, effective=1000, saved=1000) - >>> p.username() - 'giampaolo' - >>> p.communicate() - ('hi', None) - >>> p.terminate() - >>> p.wait(timeout=2) - 0 - >>> - """ - - def __init__(self, *args, **kwargs): - # Explicitly avoid to raise NoSuchProcess in case the process - # spawned by subprocess.Popen terminates too quickly, see: - # https://github.com/giampaolo/psutil/issues/193 - self.__subproc = subprocess.Popen(*args, **kwargs) - self._init(self.__subproc.pid, _ignore_nsp=True) - - def __dir__(self): - return sorted(set(dir(Popen) + dir(subprocess.Popen))) - - def __enter__(self): - if hasattr(self.__subproc, '__enter__'): - self.__subproc.__enter__() - return self - - def __exit__(self, *args, **kwargs): - if hasattr(self.__subproc, '__exit__'): - return self.__subproc.__exit__(*args, **kwargs) - else: - if self.stdout: - self.stdout.close() - if self.stderr: - self.stderr.close() - try: - # Flushing a BufferedWriter may raise an error. - if self.stdin: - self.stdin.close() - finally: - # Wait for the process to terminate, to avoid zombies. - self.wait() - - def __getattribute__(self, name): - try: - return object.__getattribute__(self, name) - except AttributeError: - try: - return object.__getattribute__(self.__subproc, name) - except AttributeError: - msg = f"{self.__class__!r} has no attribute {name!r}" - raise AttributeError(msg) from None - - def wait(self, timeout=None): - if self.__subproc.returncode is not None: - return self.__subproc.returncode - ret = super().wait(timeout) - self.__subproc.returncode = ret - return ret - - -# ===================================================================== -# --- system processes related functions -# ===================================================================== - - -def pids(): - """Return a list of current running PIDs.""" - global _LOWEST_PID - ret = sorted(_psplatform.pids()) - _LOWEST_PID = ret[0] - return ret - - -def pid_exists(pid): - """Return True if given PID exists in the current process list. - This is faster than doing "pid in psutil.pids()" and - should be preferred. - """ - if pid < 0: - return False - elif pid == 0 and POSIX: - # On POSIX we use os.kill() to determine PID existence. - # According to "man 2 kill" PID 0 has a special meaning - # though: it refers to <> and that is not we want - # to do here. - return pid in pids() - else: - return _psplatform.pid_exists(pid) - - -_pmap = {} -_pids_reused = set() - - -def process_iter(attrs=None, ad_value=None): - """Return a generator yielding a Process instance for all - running processes. - - Every new Process instance is only created once and then cached - into an internal table which is updated every time this is used. - Cache can optionally be cleared via `process_iter.clear_cache()`. - - The sorting order in which processes are yielded is based on - their PIDs. - - *attrs* and *ad_value* have the same meaning as in - Process.as_dict(). If *attrs* is specified as_dict() is called - and the resulting dict is stored as a 'info' attribute attached - to returned Process instance. - If *attrs* is an empty list it will retrieve all process info - (slow). - """ - global _pmap - - def add(pid): - proc = Process(pid) - pmap[proc.pid] = proc - return proc - - def remove(pid): - pmap.pop(pid, None) - - pmap = _pmap.copy() - a = set(pids()) - b = set(pmap.keys()) - new_pids = a - b - gone_pids = b - a - for pid in gone_pids: - remove(pid) - while _pids_reused: - pid = _pids_reused.pop() - debug(f"refreshing Process instance for reused PID {pid}") - remove(pid) - try: - ls = sorted(list(pmap.items()) + list(dict.fromkeys(new_pids).items())) - for pid, proc in ls: - try: - if proc is None: # new process - proc = add(pid) - if attrs is not None: - proc.info = proc.as_dict(attrs=attrs, ad_value=ad_value) - yield proc - except NoSuchProcess: - remove(pid) - finally: - _pmap = pmap - - -process_iter.cache_clear = lambda: _pmap.clear() # noqa: PLW0108 -process_iter.cache_clear.__doc__ = "Clear process_iter() internal cache." - - -def wait_procs(procs, timeout=None, callback=None): - """Convenience function which waits for a list of processes to - terminate. - - Return a (gone, alive) tuple indicating which processes - are gone and which ones are still alive. - - The gone ones will have a new *returncode* attribute indicating - process exit status (may be None). - - *callback* is a function which gets called every time a process - terminates (a Process instance is passed as callback argument). - - Function will return as soon as all processes terminate or when - *timeout* occurs. - Differently from Process.wait() it will not raise TimeoutExpired if - *timeout* occurs. - - Typical use case is: - - - send SIGTERM to a list of processes - - give them some time to terminate - - send SIGKILL to those ones which are still alive - - Example: - - >>> def on_terminate(proc): - ... print("process {} terminated".format(proc)) - ... - >>> for p in procs: - ... p.terminate() - ... - >>> gone, alive = wait_procs(procs, timeout=3, callback=on_terminate) - >>> for p in alive: - ... p.kill() - """ - - def check_gone(proc, timeout): - try: - returncode = proc.wait(timeout=timeout) - except (TimeoutExpired, subprocess.TimeoutExpired): - pass - else: - if returncode is not None or not proc.is_running(): - # Set new Process instance attribute. - proc.returncode = returncode - gone.add(proc) - if callback is not None: - callback(proc) - - if timeout is not None and not timeout >= 0: - msg = f"timeout must be a positive integer, got {timeout}" - raise ValueError(msg) - gone = set() - alive = set(procs) - if callback is not None and not callable(callback): - msg = f"callback {callback!r} is not a callable" - raise TypeError(msg) - if timeout is not None: - deadline = _timer() + timeout - - while alive: - if timeout is not None and timeout <= 0: - break - for proc in alive: - # Make sure that every complete iteration (all processes) - # will last max 1 sec. - # We do this because we don't want to wait too long on a - # single process: in case it terminates too late other - # processes may disappear in the meantime and their PID - # reused. - max_timeout = 1.0 / len(alive) - if timeout is not None: - timeout = min((deadline - _timer()), max_timeout) - if timeout <= 0: - break - check_gone(proc, timeout) - else: - check_gone(proc, max_timeout) - alive = alive - gone # noqa: PLR6104 - - if alive: - # Last attempt over processes survived so far. - # timeout == 0 won't make this function wait any further. - for proc in alive: - check_gone(proc, 0) - alive = alive - gone # noqa: PLR6104 - - return (list(gone), list(alive)) - - -# ===================================================================== -# --- CPU related functions -# ===================================================================== - - -def cpu_count(logical=True): - """Return the number of logical CPUs in the system (same as - os.cpu_count()). - - If *logical* is False return the number of physical cores only - (e.g. hyper thread CPUs are excluded). - - Return None if undetermined. - - The return value is cached after first call. - If desired cache can be cleared like this: - - >>> psutil.cpu_count.cache_clear() - """ - if logical: - ret = _psplatform.cpu_count_logical() - else: - ret = _psplatform.cpu_count_cores() - if ret is not None and ret < 1: - ret = None - return ret - - -def cpu_times(percpu=False): - """Return system-wide CPU times as a namedtuple. - Every CPU time represents the seconds the CPU has spent in the - given mode. The namedtuple's fields availability varies depending on the - platform: - - - user - - system - - idle - - nice (UNIX) - - iowait (Linux) - - irq (Linux, FreeBSD) - - softirq (Linux) - - steal (Linux >= 2.6.11) - - guest (Linux >= 2.6.24) - - guest_nice (Linux >= 3.2.0) - - When *percpu* is True return a list of namedtuples for each CPU. - First element of the list refers to first CPU, second element - to second CPU and so on. - The order of the list is consistent across calls. - """ - if not percpu: - return _psplatform.cpu_times() - else: - return _psplatform.per_cpu_times() - - -try: - _last_cpu_times = {threading.current_thread().ident: cpu_times()} -except Exception: # noqa: BLE001 - # Don't want to crash at import time. - _last_cpu_times = {} - -try: - _last_per_cpu_times = { - threading.current_thread().ident: cpu_times(percpu=True) - } -except Exception: # noqa: BLE001 - # Don't want to crash at import time. - _last_per_cpu_times = {} - - -def _cpu_tot_time(times): - """Given a cpu_time() ntuple calculates the total CPU time - (including idle time). - """ - tot = sum(times) - if LINUX: - # On Linux guest times are already accounted in "user" or - # "nice" times, so we subtract them from total. - # Htop does the same. References: - # https://github.com/giampaolo/psutil/pull/940 - # http://unix.stackexchange.com/questions/178045 - # https://github.com/torvalds/linux/blob/ - # 447976ef4fd09b1be88b316d1a81553f1aa7cd07/kernel/sched/ - # cputime.c#L158 - tot -= getattr(times, "guest", 0) # Linux 2.6.24+ - tot -= getattr(times, "guest_nice", 0) # Linux 3.2.0+ - return tot - - -def _cpu_busy_time(times): - """Given a cpu_time() ntuple calculates the busy CPU time. - We do so by subtracting all idle CPU times. - """ - busy = _cpu_tot_time(times) - busy -= times.idle - # Linux: "iowait" is time during which the CPU does not do anything - # (waits for IO to complete). On Linux IO wait is *not* accounted - # in "idle" time so we subtract it. Htop does the same. - # References: - # https://github.com/torvalds/linux/blob/ - # 447976ef4fd09b1be88b316d1a81553f1aa7cd07/kernel/sched/cputime.c#L244 - busy -= getattr(times, "iowait", 0) - return busy - - -def _cpu_times_deltas(t1, t2): - assert t1._fields == t2._fields, (t1, t2) - field_deltas = [] - for field in _psplatform.scputimes._fields: - field_delta = getattr(t2, field) - getattr(t1, field) - # CPU times are always supposed to increase over time - # or at least remain the same and that's because time - # cannot go backwards. - # Surprisingly sometimes this might not be the case (at - # least on Windows and Linux), see: - # https://github.com/giampaolo/psutil/issues/392 - # https://github.com/giampaolo/psutil/issues/645 - # https://github.com/giampaolo/psutil/issues/1210 - # Trim negative deltas to zero to ignore decreasing fields. - # top does the same. Reference: - # https://gitlab.com/procps-ng/procps/blob/v3.3.12/top/top.c#L5063 - field_delta = max(0, field_delta) - field_deltas.append(field_delta) - return _psplatform.scputimes(*field_deltas) - - -def cpu_percent(interval=None, percpu=False): - """Return a float representing the current system-wide CPU - utilization as a percentage. - - When *interval* is > 0.0 compares system CPU times elapsed before - and after the interval (blocking). - - When *interval* is 0.0 or None compares system CPU times elapsed - since last call or module import, returning immediately (non - blocking). That means the first time this is called it will - return a meaningless 0.0 value which you should ignore. - In this case is recommended for accuracy that this function be - called with at least 0.1 seconds between calls. - - When *percpu* is True returns a list of floats representing the - utilization as a percentage for each CPU. - First element of the list refers to first CPU, second element - to second CPU and so on. - The order of the list is consistent across calls. - - Examples: - - >>> # blocking, system-wide - >>> psutil.cpu_percent(interval=1) - 2.0 - >>> - >>> # blocking, per-cpu - >>> psutil.cpu_percent(interval=1, percpu=True) - [2.0, 1.0] - >>> - >>> # non-blocking (percentage since last call) - >>> psutil.cpu_percent(interval=None) - 2.9 - >>> - """ - tid = threading.current_thread().ident - blocking = interval is not None and interval > 0.0 - if interval is not None and interval < 0: - msg = f"interval is not positive (got {interval})" - raise ValueError(msg) - - def calculate(t1, t2): - times_delta = _cpu_times_deltas(t1, t2) - all_delta = _cpu_tot_time(times_delta) - busy_delta = _cpu_busy_time(times_delta) - - try: - busy_perc = (busy_delta / all_delta) * 100 - except ZeroDivisionError: - return 0.0 - else: - return round(busy_perc, 1) - - # system-wide usage - if not percpu: - if blocking: - t1 = cpu_times() - time.sleep(interval) - else: - t1 = _last_cpu_times.get(tid) or cpu_times() - _last_cpu_times[tid] = cpu_times() - return calculate(t1, _last_cpu_times[tid]) - # per-cpu usage - else: - ret = [] - if blocking: - tot1 = cpu_times(percpu=True) - time.sleep(interval) - else: - tot1 = _last_per_cpu_times.get(tid) or cpu_times(percpu=True) - _last_per_cpu_times[tid] = cpu_times(percpu=True) - for t1, t2 in zip(tot1, _last_per_cpu_times[tid]): - ret.append(calculate(t1, t2)) - return ret - - -# Use a separate dict for cpu_times_percent(), so it's independent from -# cpu_percent() and they can both be used within the same program. -_last_cpu_times_2 = _last_cpu_times.copy() -_last_per_cpu_times_2 = _last_per_cpu_times.copy() - - -def cpu_times_percent(interval=None, percpu=False): - """Same as cpu_percent() but provides utilization percentages - for each specific CPU time as is returned by cpu_times(). - For instance, on Linux we'll get: - - >>> cpu_times_percent() - cpupercent(user=4.8, nice=0.0, system=4.8, idle=90.5, iowait=0.0, - irq=0.0, softirq=0.0, steal=0.0, guest=0.0, guest_nice=0.0) - >>> - - *interval* and *percpu* arguments have the same meaning as in - cpu_percent(). - """ - tid = threading.current_thread().ident - blocking = interval is not None and interval > 0.0 - if interval is not None and interval < 0: - msg = f"interval is not positive (got {interval!r})" - raise ValueError(msg) - - def calculate(t1, t2): - nums = [] - times_delta = _cpu_times_deltas(t1, t2) - all_delta = _cpu_tot_time(times_delta) - # "scale" is the value to multiply each delta with to get percentages. - # We use "max" to avoid division by zero (if all_delta is 0, then all - # fields are 0 so percentages will be 0 too. all_delta cannot be a - # fraction because cpu times are integers) - scale = 100.0 / max(1, all_delta) - for field_delta in times_delta: - field_perc = field_delta * scale - field_perc = round(field_perc, 1) - # make sure we don't return negative values or values over 100% - field_perc = min(max(0.0, field_perc), 100.0) - nums.append(field_perc) - return _psplatform.scputimes(*nums) - - # system-wide usage - if not percpu: - if blocking: - t1 = cpu_times() - time.sleep(interval) - else: - t1 = _last_cpu_times_2.get(tid) or cpu_times() - _last_cpu_times_2[tid] = cpu_times() - return calculate(t1, _last_cpu_times_2[tid]) - # per-cpu usage - else: - ret = [] - if blocking: - tot1 = cpu_times(percpu=True) - time.sleep(interval) - else: - tot1 = _last_per_cpu_times_2.get(tid) or cpu_times(percpu=True) - _last_per_cpu_times_2[tid] = cpu_times(percpu=True) - for t1, t2 in zip(tot1, _last_per_cpu_times_2[tid]): - ret.append(calculate(t1, t2)) - return ret - - -def cpu_stats(): - """Return CPU statistics.""" - return _psplatform.cpu_stats() - - -if hasattr(_psplatform, "cpu_freq"): - - def cpu_freq(percpu=False): - """Return CPU frequency as a namedtuple including current, - min and max frequency expressed in Mhz. - - If *percpu* is True and the system supports per-cpu frequency - retrieval (Linux only) a list of frequencies is returned for - each CPU. If not a list with one element is returned. - """ - ret = _psplatform.cpu_freq() - if percpu: - return ret - else: - num_cpus = float(len(ret)) - if num_cpus == 0: - return None - elif num_cpus == 1: - return ret[0] - else: - currs, mins, maxs = 0.0, 0.0, 0.0 - set_none = False - for cpu in ret: - currs += cpu.current - # On Linux if /proc/cpuinfo is used min/max are set - # to None. - if LINUX and cpu.min is None: - set_none = True - continue - mins += cpu.min - maxs += cpu.max - - current = currs / num_cpus - - if set_none: - min_ = max_ = None - else: - min_ = mins / num_cpus - max_ = maxs / num_cpus - - return _common.scpufreq(current, min_, max_) - - __all__.append("cpu_freq") - - -if hasattr(os, "getloadavg") or hasattr(_psplatform, "getloadavg"): - # Perform this hasattr check once on import time to either use the - # platform based code or proxy straight from the os module. - if hasattr(os, "getloadavg"): - getloadavg = os.getloadavg - else: - getloadavg = _psplatform.getloadavg - - __all__.append("getloadavg") - - -# ===================================================================== -# --- system memory related functions -# ===================================================================== - - -def virtual_memory(): - """Return statistics about system memory usage as a namedtuple - including the following fields, expressed in bytes: - - - total: - total physical memory available. - - - available: - the memory that can be given instantly to processes without the - system going into swap. - This is calculated by summing different memory values depending - on the platform and it is supposed to be used to monitor actual - memory usage in a cross platform fashion. - - - percent: - the percentage usage calculated as (total - available) / total * 100 - - - used: - memory used, calculated differently depending on the platform and - designed for informational purposes only: - macOS: active + wired - BSD: active + wired + cached - Linux: total - free - - - free: - memory not being used at all (zeroed) that is readily available; - note that this doesn't reflect the actual memory available - (use 'available' instead) - - Platform-specific fields: - - - active (UNIX): - memory currently in use or very recently used, and so it is in RAM. - - - inactive (UNIX): - memory that is marked as not used. - - - buffers (BSD, Linux): - cache for things like file system metadata. - - - cached (BSD, macOS): - cache for various things. - - - wired (macOS, BSD): - memory that is marked to always stay in RAM. It is never moved to disk. - - - shared (BSD): - memory that may be simultaneously accessed by multiple processes. - - The sum of 'used' and 'available' does not necessarily equal total. - On Windows 'available' and 'free' are the same. - """ - global _TOTAL_PHYMEM - ret = _psplatform.virtual_memory() - # cached for later use in Process.memory_percent() - _TOTAL_PHYMEM = ret.total - return ret - - -def swap_memory(): - """Return system swap memory statistics as a namedtuple including - the following fields: - - - total: total swap memory in bytes - - used: used swap memory in bytes - - free: free swap memory in bytes - - percent: the percentage usage - - sin: no. of bytes the system has swapped in from disk (cumulative) - - sout: no. of bytes the system has swapped out from disk (cumulative) - - 'sin' and 'sout' on Windows are meaningless and always set to 0. - """ - return _psplatform.swap_memory() - - -# ===================================================================== -# --- disks/partitions related functions -# ===================================================================== - - -def disk_usage(path): - """Return disk usage statistics about the given *path* as a - namedtuple including total, used and free space expressed in bytes - plus the percentage usage. - """ - return _psplatform.disk_usage(path) - - -def disk_partitions(all=False): - """Return mounted partitions as a list of - (device, mountpoint, fstype, opts) namedtuple. - 'opts' field is a raw string separated by commas indicating mount - options which may vary depending on the platform. - - If *all* parameter is False return physical devices only and ignore - all others. - """ - return _psplatform.disk_partitions(all) - - -def disk_io_counters(perdisk=False, nowrap=True): - """Return system disk I/O statistics as a namedtuple including - the following fields: - - - read_count: number of reads - - write_count: number of writes - - read_bytes: number of bytes read - - write_bytes: number of bytes written - - read_time: time spent reading from disk (in ms) - - write_time: time spent writing to disk (in ms) - - Platform specific: - - - busy_time: (Linux, FreeBSD) time spent doing actual I/Os (in ms) - - read_merged_count (Linux): number of merged reads - - write_merged_count (Linux): number of merged writes - - If *perdisk* is True return the same information for every - physical disk installed on the system as a dictionary - with partition names as the keys and the namedtuple - described above as the values. - - If *nowrap* is True it detects and adjust the numbers which overflow - and wrap (restart from 0) and add "old value" to "new value" so that - the returned numbers will always be increasing or remain the same, - but never decrease. - "disk_io_counters.cache_clear()" can be used to invalidate the - cache. - - On recent Windows versions 'diskperf -y' command may need to be - executed first otherwise this function won't find any disk. - """ - kwargs = dict(perdisk=perdisk) if LINUX else {} - rawdict = _psplatform.disk_io_counters(**kwargs) - if not rawdict: - return {} if perdisk else None - if nowrap: - rawdict = _wrap_numbers(rawdict, 'psutil.disk_io_counters') - nt = getattr(_psplatform, "sdiskio", _common.sdiskio) - if perdisk: - for disk, fields in rawdict.items(): - rawdict[disk] = nt(*fields) - return rawdict - else: - return nt(*(sum(x) for x in zip(*rawdict.values()))) - - -disk_io_counters.cache_clear = functools.partial( - _wrap_numbers.cache_clear, 'psutil.disk_io_counters' -) -disk_io_counters.cache_clear.__doc__ = "Clears nowrap argument cache" - - -# ===================================================================== -# --- network related functions -# ===================================================================== - - -def net_io_counters(pernic=False, nowrap=True): - """Return network I/O statistics as a namedtuple including - the following fields: - - - bytes_sent: number of bytes sent - - bytes_recv: number of bytes received - - packets_sent: number of packets sent - - packets_recv: number of packets received - - errin: total number of errors while receiving - - errout: total number of errors while sending - - dropin: total number of incoming packets which were dropped - - dropout: total number of outgoing packets which were dropped - (always 0 on macOS and BSD) - - If *pernic* is True return the same information for every - network interface installed on the system as a dictionary - with network interface names as the keys and the namedtuple - described above as the values. - - If *nowrap* is True it detects and adjust the numbers which overflow - and wrap (restart from 0) and add "old value" to "new value" so that - the returned numbers will always be increasing or remain the same, - but never decrease. - "net_io_counters.cache_clear()" can be used to invalidate the - cache. - """ - rawdict = _psplatform.net_io_counters() - if not rawdict: - return {} if pernic else None - if nowrap: - rawdict = _wrap_numbers(rawdict, 'psutil.net_io_counters') - if pernic: - for nic, fields in rawdict.items(): - rawdict[nic] = _common.snetio(*fields) - return rawdict - else: - return _common.snetio(*[sum(x) for x in zip(*rawdict.values())]) - - -net_io_counters.cache_clear = functools.partial( - _wrap_numbers.cache_clear, 'psutil.net_io_counters' -) -net_io_counters.cache_clear.__doc__ = "Clears nowrap argument cache" - - -def net_connections(kind='inet'): - """Return system-wide socket connections as a list of - (fd, family, type, laddr, raddr, status, pid) namedtuples. - In case of limited privileges 'fd' and 'pid' may be set to -1 - and None respectively. - The *kind* parameter filters for connections that fit the - following criteria: - - +------------+----------------------------------------------------+ - | Kind Value | Connections using | - +------------+----------------------------------------------------+ - | inet | IPv4 and IPv6 | - | inet4 | IPv4 | - | inet6 | IPv6 | - | tcp | TCP | - | tcp4 | TCP over IPv4 | - | tcp6 | TCP over IPv6 | - | udp | UDP | - | udp4 | UDP over IPv4 | - | udp6 | UDP over IPv6 | - | unix | UNIX socket (both UDP and TCP protocols) | - | all | the sum of all the possible families and protocols | - +------------+----------------------------------------------------+ - - On macOS this function requires root privileges. - """ - _check_conn_kind(kind) - return _psplatform.net_connections(kind) - - -def net_if_addrs(): - """Return the addresses associated to each NIC (network interface - card) installed on the system as a dictionary whose keys are the - NIC names and value is a list of namedtuples for each address - assigned to the NIC. Each namedtuple includes 5 fields: - - - family: can be either socket.AF_INET, socket.AF_INET6 or - psutil.AF_LINK, which refers to a MAC address. - - address: is the primary address and it is always set. - - netmask: and 'broadcast' and 'ptp' may be None. - - ptp: stands for "point to point" and references the - destination address on a point to point interface - (typically a VPN). - - broadcast: and *ptp* are mutually exclusive. - - Note: you can have more than one address of the same family - associated with each interface. - """ - rawlist = _psplatform.net_if_addrs() - rawlist.sort(key=lambda x: x[1]) # sort by family - ret = collections.defaultdict(list) - for name, fam, addr, mask, broadcast, ptp in rawlist: - try: - fam = socket.AddressFamily(fam) - except ValueError: - if WINDOWS and fam == -1: - fam = _psplatform.AF_LINK - elif ( - hasattr(_psplatform, "AF_LINK") and fam == _psplatform.AF_LINK - ): - # Linux defines AF_LINK as an alias for AF_PACKET. - # We re-set the family here so that repr(family) - # will show AF_LINK rather than AF_PACKET - fam = _psplatform.AF_LINK - - if fam == _psplatform.AF_LINK: - # The underlying C function may return an incomplete MAC - # address in which case we fill it with null bytes, see: - # https://github.com/giampaolo/psutil/issues/786 - separator = ":" if POSIX else "-" - while addr.count(separator) < 5: - addr += f"{separator}00" - - nt = _common.snicaddr(fam, addr, mask, broadcast, ptp) - - # On Windows broadcast is None, so we determine it via - # ipaddress module. - if WINDOWS and fam in {socket.AF_INET, socket.AF_INET6}: - try: - broadcast = _common.broadcast_addr(nt) - except Exception as err: # noqa: BLE001 - debug(err) - else: - if broadcast is not None: - nt._replace(broadcast=broadcast) - - ret[name].append(nt) - - return dict(ret) - - -def net_if_stats(): - """Return information about each NIC (network interface card) - installed on the system as a dictionary whose keys are the - NIC names and value is a namedtuple with the following fields: - - - isup: whether the interface is up (bool) - - duplex: can be either NIC_DUPLEX_FULL, NIC_DUPLEX_HALF or - NIC_DUPLEX_UNKNOWN - - speed: the NIC speed expressed in mega bits (MB); if it can't - be determined (e.g. 'localhost') it will be set to 0. - - mtu: the maximum transmission unit expressed in bytes. - """ - return _psplatform.net_if_stats() - - -# ===================================================================== -# --- sensors -# ===================================================================== - - -# Linux, macOS -if hasattr(_psplatform, "sensors_temperatures"): - - def sensors_temperatures(fahrenheit=False): - """Return hardware temperatures. Each entry is a namedtuple - representing a certain hardware sensor (it may be a CPU, an - hard disk or something else, depending on the OS and its - configuration). - All temperatures are expressed in celsius unless *fahrenheit* - is set to True. - """ - - def convert(n): - if n is not None: - return (float(n) * 9 / 5) + 32 if fahrenheit else n - - ret = collections.defaultdict(list) - rawdict = _psplatform.sensors_temperatures() - - for name, values in rawdict.items(): - while values: - label, current, high, critical = values.pop(0) - current = convert(current) - high = convert(high) - critical = convert(critical) - - if high and not critical: - critical = high - elif critical and not high: - high = critical - - ret[name].append( - _common.shwtemp(label, current, high, critical) - ) - - return dict(ret) - - __all__.append("sensors_temperatures") - - -# Linux -if hasattr(_psplatform, "sensors_fans"): - - def sensors_fans(): - """Return fans speed. Each entry is a namedtuple - representing a certain hardware sensor. - All speed are expressed in RPM (rounds per minute). - """ - return _psplatform.sensors_fans() - - __all__.append("sensors_fans") - - -# Linux, Windows, FreeBSD, macOS -if hasattr(_psplatform, "sensors_battery"): - - def sensors_battery(): - """Return battery information. If no battery is installed - returns None. - - - percent: battery power left as a percentage. - - secsleft: a rough approximation of how many seconds are left - before the battery runs out of power. May be - POWER_TIME_UNLIMITED or POWER_TIME_UNLIMITED. - - power_plugged: True if the AC power cable is connected. - """ - return _psplatform.sensors_battery() - - __all__.append("sensors_battery") - - -# ===================================================================== -# --- other system related functions -# ===================================================================== - - -def boot_time(): - """Return the system boot time expressed in seconds since the epoch.""" - # Note: we are not caching this because it is subject to - # system clock updates. - return _psplatform.boot_time() - - -def users(): - """Return users currently connected on the system as a list of - namedtuples including the following fields. - - - user: the name of the user - - terminal: the tty or pseudo-tty associated with the user, if any. - - host: the host name associated with the entry, if any. - - started: the creation time as a floating point number expressed in - seconds since the epoch. - """ - return _psplatform.users() - - -# ===================================================================== -# --- Windows services -# ===================================================================== - - -if WINDOWS: - - def win_service_iter(): - """Return a generator yielding a WindowsService instance for all - Windows services installed. - """ - return _psplatform.win_service_iter() - - def win_service_get(name): - """Get a Windows service by *name*. - Raise NoSuchProcess if no service with such name exists. - """ - return _psplatform.win_service_get(name) - - -# ===================================================================== - - -def _set_debug(value): - """Enable or disable PSUTIL_DEBUG option, which prints debugging - messages to stderr. - """ - import psutil._common - - psutil._common.PSUTIL_DEBUG = bool(value) - _psplatform.cext.set_debug(bool(value)) - - -del memoize_when_activated diff --git a/PortablePython/Lib/site-packages/psutil/_common.py b/PortablePython/Lib/site-packages/psutil/_common.py deleted file mode 100644 index 4096c0a..0000000 --- a/PortablePython/Lib/site-packages/psutil/_common.py +++ /dev/null @@ -1,950 +0,0 @@ -# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -"""Common objects shared by __init__.py and _ps*.py modules. - -Note: this module is imported by setup.py, so it should not import -psutil or third-party modules. -""" - -import collections -import enum -import functools -import os -import socket -import stat -import sys -import threading -import warnings -from collections import namedtuple -from socket import AF_INET -from socket import SOCK_DGRAM -from socket import SOCK_STREAM - - -try: - from socket import AF_INET6 -except ImportError: - AF_INET6 = None -try: - from socket import AF_UNIX -except ImportError: - AF_UNIX = None - - -PSUTIL_DEBUG = bool(os.getenv('PSUTIL_DEBUG')) -_DEFAULT = object() - -# fmt: off -__all__ = [ - # OS constants - 'FREEBSD', 'BSD', 'LINUX', 'NETBSD', 'OPENBSD', 'MACOS', 'OSX', 'POSIX', - 'SUNOS', 'WINDOWS', - # connection constants - 'CONN_CLOSE', 'CONN_CLOSE_WAIT', 'CONN_CLOSING', 'CONN_ESTABLISHED', - 'CONN_FIN_WAIT1', 'CONN_FIN_WAIT2', 'CONN_LAST_ACK', 'CONN_LISTEN', - 'CONN_NONE', 'CONN_SYN_RECV', 'CONN_SYN_SENT', 'CONN_TIME_WAIT', - # net constants - 'NIC_DUPLEX_FULL', 'NIC_DUPLEX_HALF', 'NIC_DUPLEX_UNKNOWN', # noqa: F822 - # process status constants - 'STATUS_DEAD', 'STATUS_DISK_SLEEP', 'STATUS_IDLE', 'STATUS_LOCKED', - 'STATUS_RUNNING', 'STATUS_SLEEPING', 'STATUS_STOPPED', 'STATUS_SUSPENDED', - 'STATUS_TRACING_STOP', 'STATUS_WAITING', 'STATUS_WAKE_KILL', - 'STATUS_WAKING', 'STATUS_ZOMBIE', 'STATUS_PARKED', - # other constants - 'ENCODING', 'ENCODING_ERRS', 'AF_INET6', - # named tuples - 'pconn', 'pcputimes', 'pctxsw', 'pgids', 'pio', 'pionice', 'popenfile', - 'pthread', 'puids', 'sconn', 'scpustats', 'sdiskio', 'sdiskpart', - 'sdiskusage', 'snetio', 'snicaddr', 'snicstats', 'sswap', 'suser', - # utility functions - 'conn_tmap', 'deprecated_method', 'isfile_strict', 'memoize', - 'parse_environ_block', 'path_exists_strict', 'usage_percent', - 'supports_ipv6', 'sockfam_to_enum', 'socktype_to_enum', "wrap_numbers", - 'open_text', 'open_binary', 'cat', 'bcat', - 'bytes2human', 'conn_to_ntuple', 'debug', - # shell utils - 'hilite', 'term_supports_colors', 'print_color', -] -# fmt: on - - -# =================================================================== -# --- OS constants -# =================================================================== - - -POSIX = os.name == "posix" -WINDOWS = os.name == "nt" -LINUX = sys.platform.startswith("linux") -MACOS = sys.platform.startswith("darwin") -OSX = MACOS # deprecated alias -FREEBSD = sys.platform.startswith(("freebsd", "midnightbsd")) -OPENBSD = sys.platform.startswith("openbsd") -NETBSD = sys.platform.startswith("netbsd") -BSD = FREEBSD or OPENBSD or NETBSD -SUNOS = sys.platform.startswith(("sunos", "solaris")) -AIX = sys.platform.startswith("aix") - - -# =================================================================== -# --- API constants -# =================================================================== - - -# Process.status() -STATUS_RUNNING = "running" -STATUS_SLEEPING = "sleeping" -STATUS_DISK_SLEEP = "disk-sleep" -STATUS_STOPPED = "stopped" -STATUS_TRACING_STOP = "tracing-stop" -STATUS_ZOMBIE = "zombie" -STATUS_DEAD = "dead" -STATUS_WAKE_KILL = "wake-kill" -STATUS_WAKING = "waking" -STATUS_IDLE = "idle" # Linux, macOS, FreeBSD -STATUS_LOCKED = "locked" # FreeBSD -STATUS_WAITING = "waiting" # FreeBSD -STATUS_SUSPENDED = "suspended" # NetBSD -STATUS_PARKED = "parked" # Linux - -# Process.net_connections() and psutil.net_connections() -CONN_ESTABLISHED = "ESTABLISHED" -CONN_SYN_SENT = "SYN_SENT" -CONN_SYN_RECV = "SYN_RECV" -CONN_FIN_WAIT1 = "FIN_WAIT1" -CONN_FIN_WAIT2 = "FIN_WAIT2" -CONN_TIME_WAIT = "TIME_WAIT" -CONN_CLOSE = "CLOSE" -CONN_CLOSE_WAIT = "CLOSE_WAIT" -CONN_LAST_ACK = "LAST_ACK" -CONN_LISTEN = "LISTEN" -CONN_CLOSING = "CLOSING" -CONN_NONE = "NONE" - - -# net_if_stats() -class NicDuplex(enum.IntEnum): - NIC_DUPLEX_FULL = 2 - NIC_DUPLEX_HALF = 1 - NIC_DUPLEX_UNKNOWN = 0 - - -globals().update(NicDuplex.__members__) - - -# sensors_battery() -class BatteryTime(enum.IntEnum): - POWER_TIME_UNKNOWN = -1 - POWER_TIME_UNLIMITED = -2 - - -globals().update(BatteryTime.__members__) - -# --- others - -ENCODING = sys.getfilesystemencoding() -ENCODING_ERRS = sys.getfilesystemencodeerrors() - - -# =================================================================== -# --- namedtuples -# =================================================================== - -# --- for system functions - -# fmt: off -# psutil.swap_memory() -sswap = namedtuple('sswap', ['total', 'used', 'free', 'percent', 'sin', - 'sout']) -# psutil.disk_usage() -sdiskusage = namedtuple('sdiskusage', ['total', 'used', 'free', 'percent']) -# psutil.disk_io_counters() -sdiskio = namedtuple('sdiskio', ['read_count', 'write_count', - 'read_bytes', 'write_bytes', - 'read_time', 'write_time']) -# psutil.disk_partitions() -sdiskpart = namedtuple('sdiskpart', ['device', 'mountpoint', 'fstype', 'opts']) -# psutil.net_io_counters() -snetio = namedtuple('snetio', ['bytes_sent', 'bytes_recv', - 'packets_sent', 'packets_recv', - 'errin', 'errout', - 'dropin', 'dropout']) -# psutil.users() -suser = namedtuple('suser', ['name', 'terminal', 'host', 'started', 'pid']) -# psutil.net_connections() -sconn = namedtuple('sconn', ['fd', 'family', 'type', 'laddr', 'raddr', - 'status', 'pid']) -# psutil.net_if_addrs() -snicaddr = namedtuple('snicaddr', - ['family', 'address', 'netmask', 'broadcast', 'ptp']) -# psutil.net_if_stats() -snicstats = namedtuple('snicstats', - ['isup', 'duplex', 'speed', 'mtu', 'flags']) -# psutil.cpu_stats() -scpustats = namedtuple( - 'scpustats', ['ctx_switches', 'interrupts', 'soft_interrupts', 'syscalls']) -# psutil.cpu_freq() -scpufreq = namedtuple('scpufreq', ['current', 'min', 'max']) -# psutil.sensors_temperatures() -shwtemp = namedtuple( - 'shwtemp', ['label', 'current', 'high', 'critical']) -# psutil.sensors_battery() -sbattery = namedtuple('sbattery', ['percent', 'secsleft', 'power_plugged']) -# psutil.sensors_fans() -sfan = namedtuple('sfan', ['label', 'current']) -# fmt: on - -# --- for Process methods - -# psutil.Process.cpu_times() -pcputimes = namedtuple( - 'pcputimes', ['user', 'system', 'children_user', 'children_system'] -) -# psutil.Process.open_files() -popenfile = namedtuple('popenfile', ['path', 'fd']) -# psutil.Process.threads() -pthread = namedtuple('pthread', ['id', 'user_time', 'system_time']) -# psutil.Process.uids() -puids = namedtuple('puids', ['real', 'effective', 'saved']) -# psutil.Process.gids() -pgids = namedtuple('pgids', ['real', 'effective', 'saved']) -# psutil.Process.io_counters() -pio = namedtuple( - 'pio', ['read_count', 'write_count', 'read_bytes', 'write_bytes'] -) -# psutil.Process.ionice() -pionice = namedtuple('pionice', ['ioclass', 'value']) -# psutil.Process.ctx_switches() -pctxsw = namedtuple('pctxsw', ['voluntary', 'involuntary']) -# psutil.Process.net_connections() -pconn = namedtuple( - 'pconn', ['fd', 'family', 'type', 'laddr', 'raddr', 'status'] -) - -# psutil.net_connections() and psutil.Process.net_connections() -addr = namedtuple('addr', ['ip', 'port']) - - -# =================================================================== -# --- Process.net_connections() 'kind' parameter mapping -# =================================================================== - - -conn_tmap = { - "all": ([AF_INET, AF_INET6, AF_UNIX], [SOCK_STREAM, SOCK_DGRAM]), - "tcp": ([AF_INET, AF_INET6], [SOCK_STREAM]), - "tcp4": ([AF_INET], [SOCK_STREAM]), - "udp": ([AF_INET, AF_INET6], [SOCK_DGRAM]), - "udp4": ([AF_INET], [SOCK_DGRAM]), - "inet": ([AF_INET, AF_INET6], [SOCK_STREAM, SOCK_DGRAM]), - "inet4": ([AF_INET], [SOCK_STREAM, SOCK_DGRAM]), - "inet6": ([AF_INET6], [SOCK_STREAM, SOCK_DGRAM]), -} - -if AF_INET6 is not None: - conn_tmap.update({ - "tcp6": ([AF_INET6], [SOCK_STREAM]), - "udp6": ([AF_INET6], [SOCK_DGRAM]), - }) - -if AF_UNIX is not None and not SUNOS: - conn_tmap.update({"unix": ([AF_UNIX], [SOCK_STREAM, SOCK_DGRAM])}) - - -# ===================================================================== -# --- Exceptions -# ===================================================================== - - -class Error(Exception): - """Base exception class. All other psutil exceptions inherit - from this one. - """ - - __module__ = 'psutil' - - def _infodict(self, attrs): - info = collections.OrderedDict() - for name in attrs: - value = getattr(self, name, None) - if value or (name == "pid" and value == 0): - info[name] = value - return info - - def __str__(self): - # invoked on `raise Error` - info = self._infodict(("pid", "ppid", "name")) - if info: - details = "({})".format( - ", ".join([f"{k}={v!r}" for k, v in info.items()]) - ) - else: - details = None - return " ".join([x for x in (getattr(self, "msg", ""), details) if x]) - - def __repr__(self): - # invoked on `repr(Error)` - info = self._infodict(("pid", "ppid", "name", "seconds", "msg")) - details = ", ".join([f"{k}={v!r}" for k, v in info.items()]) - return f"psutil.{self.__class__.__name__}({details})" - - -class NoSuchProcess(Error): - """Exception raised when a process with a certain PID doesn't - or no longer exists. - """ - - __module__ = 'psutil' - - def __init__(self, pid, name=None, msg=None): - Error.__init__(self) - self.pid = pid - self.name = name - self.msg = msg or "process no longer exists" - - def __reduce__(self): - return (self.__class__, (self.pid, self.name, self.msg)) - - -class ZombieProcess(NoSuchProcess): - """Exception raised when querying a zombie process. This is - raised on macOS, BSD and Solaris only, and not always: depending - on the query the OS may be able to succeed anyway. - On Linux all zombie processes are querable (hence this is never - raised). Windows doesn't have zombie processes. - """ - - __module__ = 'psutil' - - def __init__(self, pid, name=None, ppid=None, msg=None): - NoSuchProcess.__init__(self, pid, name, msg) - self.ppid = ppid - self.msg = msg or "PID still exists but it's a zombie" - - def __reduce__(self): - return (self.__class__, (self.pid, self.name, self.ppid, self.msg)) - - -class AccessDenied(Error): - """Exception raised when permission to perform an action is denied.""" - - __module__ = 'psutil' - - def __init__(self, pid=None, name=None, msg=None): - Error.__init__(self) - self.pid = pid - self.name = name - self.msg = msg or "" - - def __reduce__(self): - return (self.__class__, (self.pid, self.name, self.msg)) - - -class TimeoutExpired(Error): - """Raised on Process.wait(timeout) if timeout expires and process - is still alive. - """ - - __module__ = 'psutil' - - def __init__(self, seconds, pid=None, name=None): - Error.__init__(self) - self.seconds = seconds - self.pid = pid - self.name = name - self.msg = f"timeout after {seconds} seconds" - - def __reduce__(self): - return (self.__class__, (self.seconds, self.pid, self.name)) - - -# =================================================================== -# --- utils -# =================================================================== - - -def usage_percent(used, total, round_=None): - """Calculate percentage usage of 'used' against 'total'.""" - try: - ret = (float(used) / total) * 100 - except ZeroDivisionError: - return 0.0 - else: - if round_ is not None: - ret = round(ret, round_) - return ret - - -def memoize(fun): - """A simple memoize decorator for functions supporting (hashable) - positional arguments. - It also provides a cache_clear() function for clearing the cache: - - >>> @memoize - ... def foo() - ... return 1 - ... - >>> foo() - 1 - >>> foo.cache_clear() - >>> - - It supports: - - functions - - classes (acts as a @singleton) - - staticmethods - - classmethods - - It does NOT support: - - methods - """ - - @functools.wraps(fun) - def wrapper(*args, **kwargs): - key = (args, frozenset(sorted(kwargs.items()))) - try: - return cache[key] - except KeyError: - try: - ret = cache[key] = fun(*args, **kwargs) - except Exception as err: # noqa: BLE001 - raise err from None - return ret - - def cache_clear(): - """Clear cache.""" - cache.clear() - - cache = {} - wrapper.cache_clear = cache_clear - return wrapper - - -def memoize_when_activated(fun): - """A memoize decorator which is disabled by default. It can be - activated and deactivated on request. - For efficiency reasons it can be used only against class methods - accepting no arguments. - - >>> class Foo: - ... @memoize - ... def foo() - ... print(1) - ... - >>> f = Foo() - >>> # deactivated (default) - >>> foo() - 1 - >>> foo() - 1 - >>> - >>> # activated - >>> foo.cache_activate(self) - >>> foo() - 1 - >>> foo() - >>> foo() - >>> - """ - - @functools.wraps(fun) - def wrapper(self): - try: - # case 1: we previously entered oneshot() ctx - ret = self._cache[fun] - except AttributeError: - # case 2: we never entered oneshot() ctx - try: - return fun(self) - except Exception as err: # noqa: BLE001 - raise err from None - except KeyError: - # case 3: we entered oneshot() ctx but there's no cache - # for this entry yet - try: - ret = fun(self) - except Exception as err: # noqa: BLE001 - raise err from None - try: - self._cache[fun] = ret - except AttributeError: - # multi-threading race condition, see: - # https://github.com/giampaolo/psutil/issues/1948 - pass - return ret - - def cache_activate(proc): - """Activate cache. Expects a Process instance. Cache will be - stored as a "_cache" instance attribute. - """ - proc._cache = {} - - def cache_deactivate(proc): - """Deactivate and clear cache.""" - try: - del proc._cache - except AttributeError: - pass - - wrapper.cache_activate = cache_activate - wrapper.cache_deactivate = cache_deactivate - return wrapper - - -def isfile_strict(path): - """Same as os.path.isfile() but does not swallow EACCES / EPERM - exceptions, see: - http://mail.python.org/pipermail/python-dev/2012-June/120787.html. - """ - try: - st = os.stat(path) - except PermissionError: - raise - except OSError: - return False - else: - return stat.S_ISREG(st.st_mode) - - -def path_exists_strict(path): - """Same as os.path.exists() but does not swallow EACCES / EPERM - exceptions. See: - http://mail.python.org/pipermail/python-dev/2012-June/120787.html. - """ - try: - os.stat(path) - except PermissionError: - raise - except OSError: - return False - else: - return True - - -@memoize -def supports_ipv6(): - """Return True if IPv6 is supported on this platform.""" - if not socket.has_ipv6 or AF_INET6 is None: - return False - try: - with socket.socket(AF_INET6, socket.SOCK_STREAM) as sock: - sock.bind(("::1", 0)) - return True - except OSError: - return False - - -def parse_environ_block(data): - """Parse a C environ block of environment variables into a dictionary.""" - # The block is usually raw data from the target process. It might contain - # trailing garbage and lines that do not look like assignments. - ret = {} - pos = 0 - - # localize global variable to speed up access. - WINDOWS_ = WINDOWS - while True: - next_pos = data.find("\0", pos) - # nul byte at the beginning or double nul byte means finish - if next_pos <= pos: - break - # there might not be an equals sign - equal_pos = data.find("=", pos, next_pos) - if equal_pos > pos: - key = data[pos:equal_pos] - value = data[equal_pos + 1 : next_pos] - # Windows expects environment variables to be uppercase only - if WINDOWS_: - key = key.upper() - ret[key] = value - pos = next_pos + 1 - - return ret - - -def sockfam_to_enum(num): - """Convert a numeric socket family value to an IntEnum member. - If it's not a known member, return the numeric value itself. - """ - try: - return socket.AddressFamily(num) - except ValueError: - return num - - -def socktype_to_enum(num): - """Convert a numeric socket type value to an IntEnum member. - If it's not a known member, return the numeric value itself. - """ - try: - return socket.SocketKind(num) - except ValueError: - return num - - -def conn_to_ntuple(fd, fam, type_, laddr, raddr, status, status_map, pid=None): - """Convert a raw connection tuple to a proper ntuple.""" - if fam in {socket.AF_INET, AF_INET6}: - if laddr: - laddr = addr(*laddr) - if raddr: - raddr = addr(*raddr) - if type_ == socket.SOCK_STREAM and fam in {AF_INET, AF_INET6}: - status = status_map.get(status, CONN_NONE) - else: - status = CONN_NONE # ignore whatever C returned to us - fam = sockfam_to_enum(fam) - type_ = socktype_to_enum(type_) - if pid is None: - return pconn(fd, fam, type_, laddr, raddr, status) - else: - return sconn(fd, fam, type_, laddr, raddr, status, pid) - - -def broadcast_addr(addr): - """Given the address ntuple returned by ``net_if_addrs()`` - calculates the broadcast address. - """ - import ipaddress - - if not addr.address or not addr.netmask: - return None - if addr.family == socket.AF_INET: - return str( - ipaddress.IPv4Network( - f"{addr.address}/{addr.netmask}", strict=False - ).broadcast_address - ) - if addr.family == socket.AF_INET6: - return str( - ipaddress.IPv6Network( - f"{addr.address}/{addr.netmask}", strict=False - ).broadcast_address - ) - - -def deprecated_method(replacement): - """A decorator which can be used to mark a method as deprecated - 'replcement' is the method name which will be called instead. - """ - - def outer(fun): - msg = ( - f"{fun.__name__}() is deprecated and will be removed; use" - f" {replacement}() instead" - ) - if fun.__doc__ is None: - fun.__doc__ = msg - - @functools.wraps(fun) - def inner(self, *args, **kwargs): - warnings.warn(msg, category=DeprecationWarning, stacklevel=2) - return getattr(self, replacement)(*args, **kwargs) - - return inner - - return outer - - -class _WrapNumbers: - """Watches numbers so that they don't overflow and wrap - (reset to zero). - """ - - def __init__(self): - self.lock = threading.Lock() - self.cache = {} - self.reminders = {} - self.reminder_keys = {} - - def _add_dict(self, input_dict, name): - assert name not in self.cache - assert name not in self.reminders - assert name not in self.reminder_keys - self.cache[name] = input_dict - self.reminders[name] = collections.defaultdict(int) - self.reminder_keys[name] = collections.defaultdict(set) - - def _remove_dead_reminders(self, input_dict, name): - """In case the number of keys changed between calls (e.g. a - disk disappears) this removes the entry from self.reminders. - """ - old_dict = self.cache[name] - gone_keys = set(old_dict.keys()) - set(input_dict.keys()) - for gone_key in gone_keys: - for remkey in self.reminder_keys[name][gone_key]: - del self.reminders[name][remkey] - del self.reminder_keys[name][gone_key] - - def run(self, input_dict, name): - """Cache dict and sum numbers which overflow and wrap. - Return an updated copy of `input_dict`. - """ - if name not in self.cache: - # This was the first call. - self._add_dict(input_dict, name) - return input_dict - - self._remove_dead_reminders(input_dict, name) - - old_dict = self.cache[name] - new_dict = {} - for key in input_dict: - input_tuple = input_dict[key] - try: - old_tuple = old_dict[key] - except KeyError: - # The input dict has a new key (e.g. a new disk or NIC) - # which didn't exist in the previous call. - new_dict[key] = input_tuple - continue - - bits = [] - for i in range(len(input_tuple)): - input_value = input_tuple[i] - old_value = old_tuple[i] - remkey = (key, i) - if input_value < old_value: - # it wrapped! - self.reminders[name][remkey] += old_value - self.reminder_keys[name][key].add(remkey) - bits.append(input_value + self.reminders[name][remkey]) - - new_dict[key] = tuple(bits) - - self.cache[name] = input_dict - return new_dict - - def cache_clear(self, name=None): - """Clear the internal cache, optionally only for function 'name'.""" - with self.lock: - if name is None: - self.cache.clear() - self.reminders.clear() - self.reminder_keys.clear() - else: - self.cache.pop(name, None) - self.reminders.pop(name, None) - self.reminder_keys.pop(name, None) - - def cache_info(self): - """Return internal cache dicts as a tuple of 3 elements.""" - with self.lock: - return (self.cache, self.reminders, self.reminder_keys) - - -def wrap_numbers(input_dict, name): - """Given an `input_dict` and a function `name`, adjust the numbers - which "wrap" (restart from zero) across different calls by adding - "old value" to "new value" and return an updated dict. - """ - with _wn.lock: - return _wn.run(input_dict, name) - - -_wn = _WrapNumbers() -wrap_numbers.cache_clear = _wn.cache_clear -wrap_numbers.cache_info = _wn.cache_info - - -# The read buffer size for open() builtin. This (also) dictates how -# much data we read(2) when iterating over file lines as in: -# >>> with open(file) as f: -# ... for line in f: -# ... ... -# Default per-line buffer size for binary files is 1K. For text files -# is 8K. We use a bigger buffer (32K) in order to have more consistent -# results when reading /proc pseudo files on Linux, see: -# https://github.com/giampaolo/psutil/issues/2050 -# https://github.com/giampaolo/psutil/issues/708 -FILE_READ_BUFFER_SIZE = 32 * 1024 - - -def open_binary(fname): - return open(fname, "rb", buffering=FILE_READ_BUFFER_SIZE) - - -def open_text(fname): - """Open a file in text mode by using the proper FS encoding and - en/decoding error handlers. - """ - # See: - # https://github.com/giampaolo/psutil/issues/675 - # https://github.com/giampaolo/psutil/pull/733 - fobj = open( # noqa: SIM115 - fname, - buffering=FILE_READ_BUFFER_SIZE, - encoding=ENCODING, - errors=ENCODING_ERRS, - ) - try: - # Dictates per-line read(2) buffer size. Defaults is 8k. See: - # https://github.com/giampaolo/psutil/issues/2050#issuecomment-1013387546 - fobj._CHUNK_SIZE = FILE_READ_BUFFER_SIZE - except AttributeError: - pass - except Exception: - fobj.close() - raise - - return fobj - - -def cat(fname, fallback=_DEFAULT, _open=open_text): - """Read entire file content and return it as a string. File is - opened in text mode. If specified, `fallback` is the value - returned in case of error, either if the file does not exist or - it can't be read(). - """ - if fallback is _DEFAULT: - with _open(fname) as f: - return f.read() - else: - try: - with _open(fname) as f: - return f.read() - except OSError: - return fallback - - -def bcat(fname, fallback=_DEFAULT): - """Same as above but opens file in binary mode.""" - return cat(fname, fallback=fallback, _open=open_binary) - - -def bytes2human(n, format="%(value).1f%(symbol)s"): - """Used by various scripts. See: https://code.activestate.com/recipes/578019-bytes-to-human-human-to-bytes-converter/?in=user-4178764. - - >>> bytes2human(10000) - '9.8K' - >>> bytes2human(100001221) - '95.4M' - """ - symbols = ('B', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y') - prefix = {} - for i, s in enumerate(symbols[1:]): - prefix[s] = 1 << (i + 1) * 10 - for symbol in reversed(symbols[1:]): - if abs(n) >= prefix[symbol]: - value = float(n) / prefix[symbol] - return format % locals() - return format % dict(symbol=symbols[0], value=n) - - -def get_procfs_path(): - """Return updated psutil.PROCFS_PATH constant.""" - return sys.modules['psutil'].PROCFS_PATH - - -def decode(s): - return s.decode(encoding=ENCODING, errors=ENCODING_ERRS) - - -# ===================================================================== -# --- shell utils -# ===================================================================== - - -@memoize -def term_supports_colors(file=sys.stdout): # pragma: no cover - if os.name == 'nt': - return True - try: - import curses - - assert file.isatty() - curses.setupterm() - assert curses.tigetnum("colors") > 0 - except Exception: # noqa: BLE001 - return False - else: - return True - - -def hilite(s, color=None, bold=False): # pragma: no cover - """Return an highlighted version of 'string'.""" - if not term_supports_colors(): - return s - attr = [] - colors = dict( - blue='34', - brown='33', - darkgrey='30', - green='32', - grey='37', - lightblue='36', - red='91', - violet='35', - yellow='93', - ) - colors[None] = '29' - try: - color = colors[color] - except KeyError: - msg = f"invalid color {color!r}; choose amongst {list(colors.keys())}" - raise ValueError(msg) from None - attr.append(color) - if bold: - attr.append('1') - return f"\x1b[{';'.join(attr)}m{s}\x1b[0m" - - -def print_color( - s, color=None, bold=False, file=sys.stdout -): # pragma: no cover - """Print a colorized version of string.""" - if not term_supports_colors(): - print(s, file=file) - elif POSIX: - print(hilite(s, color, bold), file=file) - else: - import ctypes - - DEFAULT_COLOR = 7 - GetStdHandle = ctypes.windll.Kernel32.GetStdHandle - SetConsoleTextAttribute = ( - ctypes.windll.Kernel32.SetConsoleTextAttribute - ) - - colors = dict(green=2, red=4, brown=6, yellow=6) - colors[None] = DEFAULT_COLOR - try: - color = colors[color] - except KeyError: - msg = ( - f"invalid color {color!r}; choose between" - f" {list(colors.keys())!r}" - ) - raise ValueError(msg) from None - if bold and color <= 7: - color += 8 - - handle_id = -12 if file is sys.stderr else -11 - GetStdHandle.restype = ctypes.c_ulong - handle = GetStdHandle(handle_id) - SetConsoleTextAttribute(handle, color) - try: - print(s, file=file) - finally: - SetConsoleTextAttribute(handle, DEFAULT_COLOR) - - -def debug(msg): - """If PSUTIL_DEBUG env var is set, print a debug message to stderr.""" - if PSUTIL_DEBUG: - import inspect - - fname, lineno, _, _lines, _index = inspect.getframeinfo( - inspect.currentframe().f_back - ) - if isinstance(msg, Exception): - if isinstance(msg, OSError): - # ...because str(exc) may contain info about the file name - msg = f"ignoring {msg}" - else: - msg = f"ignoring {msg!r}" - print( # noqa: T201 - f"psutil-debug [{fname}:{lineno}]> {msg}", file=sys.stderr - ) diff --git a/PortablePython/Lib/site-packages/psutil/_psaix.py b/PortablePython/Lib/site-packages/psutil/_psaix.py deleted file mode 100644 index ba2725f..0000000 --- a/PortablePython/Lib/site-packages/psutil/_psaix.py +++ /dev/null @@ -1,565 +0,0 @@ -# Copyright (c) 2009, Giampaolo Rodola' -# Copyright (c) 2017, Arnon Yaari -# All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -"""AIX platform implementation.""" - -import functools -import glob -import os -import re -import subprocess -import sys -from collections import namedtuple - -from . import _common -from . import _psposix -from . import _psutil_aix as cext -from . import _psutil_posix as cext_posix -from ._common import NIC_DUPLEX_FULL -from ._common import NIC_DUPLEX_HALF -from ._common import NIC_DUPLEX_UNKNOWN -from ._common import AccessDenied -from ._common import NoSuchProcess -from ._common import ZombieProcess -from ._common import conn_to_ntuple -from ._common import get_procfs_path -from ._common import memoize_when_activated -from ._common import usage_percent - - -__extra__all__ = ["PROCFS_PATH"] - - -# ===================================================================== -# --- globals -# ===================================================================== - - -HAS_THREADS = hasattr(cext, "proc_threads") -HAS_NET_IO_COUNTERS = hasattr(cext, "net_io_counters") -HAS_PROC_IO_COUNTERS = hasattr(cext, "proc_io_counters") - -PAGE_SIZE = cext_posix.getpagesize() -AF_LINK = cext_posix.AF_LINK - -PROC_STATUSES = { - cext.SIDL: _common.STATUS_IDLE, - cext.SZOMB: _common.STATUS_ZOMBIE, - cext.SACTIVE: _common.STATUS_RUNNING, - cext.SSWAP: _common.STATUS_RUNNING, # TODO what status is this? - cext.SSTOP: _common.STATUS_STOPPED, -} - -TCP_STATUSES = { - cext.TCPS_ESTABLISHED: _common.CONN_ESTABLISHED, - cext.TCPS_SYN_SENT: _common.CONN_SYN_SENT, - cext.TCPS_SYN_RCVD: _common.CONN_SYN_RECV, - cext.TCPS_FIN_WAIT_1: _common.CONN_FIN_WAIT1, - cext.TCPS_FIN_WAIT_2: _common.CONN_FIN_WAIT2, - cext.TCPS_TIME_WAIT: _common.CONN_TIME_WAIT, - cext.TCPS_CLOSED: _common.CONN_CLOSE, - cext.TCPS_CLOSE_WAIT: _common.CONN_CLOSE_WAIT, - cext.TCPS_LAST_ACK: _common.CONN_LAST_ACK, - cext.TCPS_LISTEN: _common.CONN_LISTEN, - cext.TCPS_CLOSING: _common.CONN_CLOSING, - cext.PSUTIL_CONN_NONE: _common.CONN_NONE, -} - -proc_info_map = dict( - ppid=0, - rss=1, - vms=2, - create_time=3, - nice=4, - num_threads=5, - status=6, - ttynr=7, -) - - -# ===================================================================== -# --- named tuples -# ===================================================================== - - -# psutil.Process.memory_info() -pmem = namedtuple('pmem', ['rss', 'vms']) -# psutil.Process.memory_full_info() -pfullmem = pmem -# psutil.Process.cpu_times() -scputimes = namedtuple('scputimes', ['user', 'system', 'idle', 'iowait']) -# psutil.virtual_memory() -svmem = namedtuple('svmem', ['total', 'available', 'percent', 'used', 'free']) - - -# ===================================================================== -# --- memory -# ===================================================================== - - -def virtual_memory(): - total, avail, free, _pinned, inuse = cext.virtual_mem() - percent = usage_percent((total - avail), total, round_=1) - return svmem(total, avail, percent, inuse, free) - - -def swap_memory(): - """Swap system memory as a (total, used, free, sin, sout) tuple.""" - total, free, sin, sout = cext.swap_mem() - used = total - free - percent = usage_percent(used, total, round_=1) - return _common.sswap(total, used, free, percent, sin, sout) - - -# ===================================================================== -# --- CPU -# ===================================================================== - - -def cpu_times(): - """Return system-wide CPU times as a named tuple.""" - ret = cext.per_cpu_times() - return scputimes(*[sum(x) for x in zip(*ret)]) - - -def per_cpu_times(): - """Return system per-CPU times as a list of named tuples.""" - ret = cext.per_cpu_times() - return [scputimes(*x) for x in ret] - - -def cpu_count_logical(): - """Return the number of logical CPUs in the system.""" - try: - return os.sysconf("SC_NPROCESSORS_ONLN") - except ValueError: - # mimic os.cpu_count() behavior - return None - - -def cpu_count_cores(): - cmd = ["lsdev", "-Cc", "processor"] - p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - stdout, stderr = p.communicate() - stdout, stderr = (x.decode(sys.stdout.encoding) for x in (stdout, stderr)) - if p.returncode != 0: - msg = f"{cmd!r} command error\n{stderr}" - raise RuntimeError(msg) - processors = stdout.strip().splitlines() - return len(processors) or None - - -def cpu_stats(): - """Return various CPU stats as a named tuple.""" - ctx_switches, interrupts, soft_interrupts, syscalls = cext.cpu_stats() - return _common.scpustats( - ctx_switches, interrupts, soft_interrupts, syscalls - ) - - -# ===================================================================== -# --- disks -# ===================================================================== - - -disk_io_counters = cext.disk_io_counters -disk_usage = _psposix.disk_usage - - -def disk_partitions(all=False): - """Return system disk partitions.""" - # TODO - the filtering logic should be better checked so that - # it tries to reflect 'df' as much as possible - retlist = [] - partitions = cext.disk_partitions() - for partition in partitions: - device, mountpoint, fstype, opts = partition - if device == 'none': - device = '' - if not all: - # Differently from, say, Linux, we don't have a list of - # common fs types so the best we can do, AFAIK, is to - # filter by filesystem having a total size > 0. - if not disk_usage(mountpoint).total: - continue - ntuple = _common.sdiskpart(device, mountpoint, fstype, opts) - retlist.append(ntuple) - return retlist - - -# ===================================================================== -# --- network -# ===================================================================== - - -net_if_addrs = cext_posix.net_if_addrs - -if HAS_NET_IO_COUNTERS: - net_io_counters = cext.net_io_counters - - -def net_connections(kind, _pid=-1): - """Return socket connections. If pid == -1 return system-wide - connections (as opposed to connections opened by one process only). - """ - families, types = _common.conn_tmap[kind] - rawlist = cext.net_connections(_pid) - ret = [] - for item in rawlist: - fd, fam, type_, laddr, raddr, status, pid = item - if fam not in families: - continue - if type_ not in types: - continue - nt = conn_to_ntuple( - fd, - fam, - type_, - laddr, - raddr, - status, - TCP_STATUSES, - pid=pid if _pid == -1 else None, - ) - ret.append(nt) - return ret - - -def net_if_stats(): - """Get NIC stats (isup, duplex, speed, mtu).""" - duplex_map = {"Full": NIC_DUPLEX_FULL, "Half": NIC_DUPLEX_HALF} - names = {x[0] for x in net_if_addrs()} - ret = {} - for name in names: - mtu = cext_posix.net_if_mtu(name) - flags = cext_posix.net_if_flags(name) - - # try to get speed and duplex - # TODO: rewrite this in C (entstat forks, so use truss -f to follow. - # looks like it is using an undocumented ioctl?) - duplex = "" - speed = 0 - p = subprocess.Popen( - ["/usr/bin/entstat", "-d", name], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - ) - stdout, stderr = p.communicate() - stdout, stderr = ( - x.decode(sys.stdout.encoding) for x in (stdout, stderr) - ) - if p.returncode == 0: - re_result = re.search( - r"Running: (\d+) Mbps.*?(\w+) Duplex", stdout - ) - if re_result is not None: - speed = int(re_result.group(1)) - duplex = re_result.group(2) - - output_flags = ','.join(flags) - isup = 'running' in flags - duplex = duplex_map.get(duplex, NIC_DUPLEX_UNKNOWN) - ret[name] = _common.snicstats(isup, duplex, speed, mtu, output_flags) - return ret - - -# ===================================================================== -# --- other system functions -# ===================================================================== - - -def boot_time(): - """The system boot time expressed in seconds since the epoch.""" - return cext.boot_time() - - -def users(): - """Return currently connected users as a list of namedtuples.""" - retlist = [] - rawlist = cext.users() - localhost = (':0.0', ':0') - for item in rawlist: - user, tty, hostname, tstamp, user_process, pid = item - # note: the underlying C function includes entries about - # system boot, run level and others. We might want - # to use them in the future. - if not user_process: - continue - if hostname in localhost: - hostname = 'localhost' - nt = _common.suser(user, tty, hostname, tstamp, pid) - retlist.append(nt) - return retlist - - -# ===================================================================== -# --- processes -# ===================================================================== - - -def pids(): - """Returns a list of PIDs currently running on the system.""" - return [int(x) for x in os.listdir(get_procfs_path()) if x.isdigit()] - - -def pid_exists(pid): - """Check for the existence of a unix pid.""" - return os.path.exists(os.path.join(get_procfs_path(), str(pid), "psinfo")) - - -def wrap_exceptions(fun): - """Call callable into a try/except clause and translate ENOENT, - EACCES and EPERM in NoSuchProcess or AccessDenied exceptions. - """ - - @functools.wraps(fun) - def wrapper(self, *args, **kwargs): - pid, ppid, name = self.pid, self._ppid, self._name - try: - return fun(self, *args, **kwargs) - except (FileNotFoundError, ProcessLookupError) as err: - # ENOENT (no such file or directory) gets raised on open(). - # ESRCH (no such process) can get raised on read() if - # process is gone in meantime. - if not pid_exists(pid): - raise NoSuchProcess(pid, name) from err - raise ZombieProcess(pid, name, ppid) from err - except PermissionError as err: - raise AccessDenied(pid, name) from err - - return wrapper - - -class Process: - """Wrapper class around underlying C implementation.""" - - __slots__ = ["_cache", "_name", "_ppid", "_procfs_path", "pid"] - - def __init__(self, pid): - self.pid = pid - self._name = None - self._ppid = None - self._procfs_path = get_procfs_path() - - def oneshot_enter(self): - self._proc_basic_info.cache_activate(self) - self._proc_cred.cache_activate(self) - - def oneshot_exit(self): - self._proc_basic_info.cache_deactivate(self) - self._proc_cred.cache_deactivate(self) - - @wrap_exceptions - @memoize_when_activated - def _proc_basic_info(self): - return cext.proc_basic_info(self.pid, self._procfs_path) - - @wrap_exceptions - @memoize_when_activated - def _proc_cred(self): - return cext.proc_cred(self.pid, self._procfs_path) - - @wrap_exceptions - def name(self): - if self.pid == 0: - return "swapper" - # note: max 16 characters - return cext.proc_name(self.pid, self._procfs_path).rstrip("\x00") - - @wrap_exceptions - def exe(self): - # there is no way to get executable path in AIX other than to guess, - # and guessing is more complex than what's in the wrapping class - cmdline = self.cmdline() - if not cmdline: - return '' - exe = cmdline[0] - if os.path.sep in exe: - # relative or absolute path - if not os.path.isabs(exe): - # if cwd has changed, we're out of luck - this may be wrong! - exe = os.path.abspath(os.path.join(self.cwd(), exe)) - if ( - os.path.isabs(exe) - and os.path.isfile(exe) - and os.access(exe, os.X_OK) - ): - return exe - # not found, move to search in PATH using basename only - exe = os.path.basename(exe) - # search for exe name PATH - for path in os.environ["PATH"].split(":"): - possible_exe = os.path.abspath(os.path.join(path, exe)) - if os.path.isfile(possible_exe) and os.access( - possible_exe, os.X_OK - ): - return possible_exe - return '' - - @wrap_exceptions - def cmdline(self): - return cext.proc_args(self.pid) - - @wrap_exceptions - def environ(self): - return cext.proc_environ(self.pid) - - @wrap_exceptions - def create_time(self): - return self._proc_basic_info()[proc_info_map['create_time']] - - @wrap_exceptions - def num_threads(self): - return self._proc_basic_info()[proc_info_map['num_threads']] - - if HAS_THREADS: - - @wrap_exceptions - def threads(self): - rawlist = cext.proc_threads(self.pid) - retlist = [] - for thread_id, utime, stime in rawlist: - ntuple = _common.pthread(thread_id, utime, stime) - retlist.append(ntuple) - # The underlying C implementation retrieves all OS threads - # and filters them by PID. At this point we can't tell whether - # an empty list means there were no connections for process or - # process is no longer active so we force NSP in case the PID - # is no longer there. - if not retlist: - # will raise NSP if process is gone - os.stat(f"{self._procfs_path}/{self.pid}") - return retlist - - @wrap_exceptions - def net_connections(self, kind='inet'): - ret = net_connections(kind, _pid=self.pid) - # The underlying C implementation retrieves all OS connections - # and filters them by PID. At this point we can't tell whether - # an empty list means there were no connections for process or - # process is no longer active so we force NSP in case the PID - # is no longer there. - if not ret: - # will raise NSP if process is gone - os.stat(f"{self._procfs_path}/{self.pid}") - return ret - - @wrap_exceptions - def nice_get(self): - return cext_posix.getpriority(self.pid) - - @wrap_exceptions - def nice_set(self, value): - return cext_posix.setpriority(self.pid, value) - - @wrap_exceptions - def ppid(self): - self._ppid = self._proc_basic_info()[proc_info_map['ppid']] - return self._ppid - - @wrap_exceptions - def uids(self): - real, effective, saved, _, _, _ = self._proc_cred() - return _common.puids(real, effective, saved) - - @wrap_exceptions - def gids(self): - _, _, _, real, effective, saved = self._proc_cred() - return _common.puids(real, effective, saved) - - @wrap_exceptions - def cpu_times(self): - t = cext.proc_cpu_times(self.pid, self._procfs_path) - return _common.pcputimes(*t) - - @wrap_exceptions - def terminal(self): - ttydev = self._proc_basic_info()[proc_info_map['ttynr']] - # convert from 64-bit dev_t to 32-bit dev_t and then map the device - ttydev = ((ttydev & 0x0000FFFF00000000) >> 16) | (ttydev & 0xFFFF) - # try to match rdev of /dev/pts/* files ttydev - for dev in glob.glob("/dev/**/*"): - if os.stat(dev).st_rdev == ttydev: - return dev - return None - - @wrap_exceptions - def cwd(self): - procfs_path = self._procfs_path - try: - result = os.readlink(f"{procfs_path}/{self.pid}/cwd") - return result.rstrip('/') - except FileNotFoundError: - os.stat(f"{procfs_path}/{self.pid}") # raise NSP or AD - return "" - - @wrap_exceptions - def memory_info(self): - ret = self._proc_basic_info() - rss = ret[proc_info_map['rss']] * 1024 - vms = ret[proc_info_map['vms']] * 1024 - return pmem(rss, vms) - - memory_full_info = memory_info - - @wrap_exceptions - def status(self): - code = self._proc_basic_info()[proc_info_map['status']] - # XXX is '?' legit? (we're not supposed to return it anyway) - return PROC_STATUSES.get(code, '?') - - def open_files(self): - # TODO rewrite without using procfiles (stat /proc/pid/fd/* and then - # find matching name of the inode) - p = subprocess.Popen( - ["/usr/bin/procfiles", "-n", str(self.pid)], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - ) - stdout, stderr = p.communicate() - stdout, stderr = ( - x.decode(sys.stdout.encoding) for x in (stdout, stderr) - ) - if "no such process" in stderr.lower(): - raise NoSuchProcess(self.pid, self._name) - procfiles = re.findall(r"(\d+): S_IFREG.*name:(.*)\n", stdout) - retlist = [] - for fd, path in procfiles: - path = path.strip() - if path.startswith("//"): - path = path[1:] - if path.lower() == "cannot be retrieved": - continue - retlist.append(_common.popenfile(path, int(fd))) - return retlist - - @wrap_exceptions - def num_fds(self): - if self.pid == 0: # no /proc/0/fd - return 0 - return len(os.listdir(f"{self._procfs_path}/{self.pid}/fd")) - - @wrap_exceptions - def num_ctx_switches(self): - return _common.pctxsw(*cext.proc_num_ctx_switches(self.pid)) - - @wrap_exceptions - def wait(self, timeout=None): - return _psposix.wait_pid(self.pid, timeout, self._name) - - if HAS_PROC_IO_COUNTERS: - - @wrap_exceptions - def io_counters(self): - try: - rc, wc, rb, wb = cext.proc_io_counters(self.pid) - except OSError as err: - # if process is terminated, proc_io_counters returns OSError - # instead of NSP - if not pid_exists(self.pid): - raise NoSuchProcess(self.pid, self._name) from err - raise - return _common.pio(rc, wc, rb, wb) diff --git a/PortablePython/Lib/site-packages/psutil/_psbsd.py b/PortablePython/Lib/site-packages/psutil/_psbsd.py deleted file mode 100644 index 13bd926..0000000 --- a/PortablePython/Lib/site-packages/psutil/_psbsd.py +++ /dev/null @@ -1,971 +0,0 @@ -# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -"""FreeBSD, OpenBSD and NetBSD platforms implementation.""" - -import contextlib -import errno -import functools -import os -from collections import defaultdict -from collections import namedtuple -from xml.etree import ElementTree # noqa: ICN001 - -from . import _common -from . import _psposix -from . import _psutil_bsd as cext -from . import _psutil_posix as cext_posix -from ._common import FREEBSD -from ._common import NETBSD -from ._common import OPENBSD -from ._common import AccessDenied -from ._common import NoSuchProcess -from ._common import ZombieProcess -from ._common import conn_tmap -from ._common import conn_to_ntuple -from ._common import debug -from ._common import memoize -from ._common import memoize_when_activated -from ._common import usage_percent - - -__extra__all__ = [] - - -# ===================================================================== -# --- globals -# ===================================================================== - - -if FREEBSD: - PROC_STATUSES = { - cext.SIDL: _common.STATUS_IDLE, - cext.SRUN: _common.STATUS_RUNNING, - cext.SSLEEP: _common.STATUS_SLEEPING, - cext.SSTOP: _common.STATUS_STOPPED, - cext.SZOMB: _common.STATUS_ZOMBIE, - cext.SWAIT: _common.STATUS_WAITING, - cext.SLOCK: _common.STATUS_LOCKED, - } -elif OPENBSD: - PROC_STATUSES = { - cext.SIDL: _common.STATUS_IDLE, - cext.SSLEEP: _common.STATUS_SLEEPING, - cext.SSTOP: _common.STATUS_STOPPED, - # According to /usr/include/sys/proc.h SZOMB is unused. - # test_zombie_process() shows that SDEAD is the right - # equivalent. Also it appears there's no equivalent of - # psutil.STATUS_DEAD. SDEAD really means STATUS_ZOMBIE. - # cext.SZOMB: _common.STATUS_ZOMBIE, - cext.SDEAD: _common.STATUS_ZOMBIE, - cext.SZOMB: _common.STATUS_ZOMBIE, - # From http://www.eecs.harvard.edu/~margo/cs161/videos/proc.h.txt - # OpenBSD has SRUN and SONPROC: SRUN indicates that a process - # is runnable but *not* yet running, i.e. is on a run queue. - # SONPROC indicates that the process is actually executing on - # a CPU, i.e. it is no longer on a run queue. - # As such we'll map SRUN to STATUS_WAKING and SONPROC to - # STATUS_RUNNING - cext.SRUN: _common.STATUS_WAKING, - cext.SONPROC: _common.STATUS_RUNNING, - } -elif NETBSD: - PROC_STATUSES = { - cext.SIDL: _common.STATUS_IDLE, - cext.SSLEEP: _common.STATUS_SLEEPING, - cext.SSTOP: _common.STATUS_STOPPED, - cext.SZOMB: _common.STATUS_ZOMBIE, - cext.SRUN: _common.STATUS_WAKING, - cext.SONPROC: _common.STATUS_RUNNING, - } - -TCP_STATUSES = { - cext.TCPS_ESTABLISHED: _common.CONN_ESTABLISHED, - cext.TCPS_SYN_SENT: _common.CONN_SYN_SENT, - cext.TCPS_SYN_RECEIVED: _common.CONN_SYN_RECV, - cext.TCPS_FIN_WAIT_1: _common.CONN_FIN_WAIT1, - cext.TCPS_FIN_WAIT_2: _common.CONN_FIN_WAIT2, - cext.TCPS_TIME_WAIT: _common.CONN_TIME_WAIT, - cext.TCPS_CLOSED: _common.CONN_CLOSE, - cext.TCPS_CLOSE_WAIT: _common.CONN_CLOSE_WAIT, - cext.TCPS_LAST_ACK: _common.CONN_LAST_ACK, - cext.TCPS_LISTEN: _common.CONN_LISTEN, - cext.TCPS_CLOSING: _common.CONN_CLOSING, - cext.PSUTIL_CONN_NONE: _common.CONN_NONE, -} - -PAGESIZE = cext_posix.getpagesize() -AF_LINK = cext_posix.AF_LINK - -HAS_PER_CPU_TIMES = hasattr(cext, "per_cpu_times") -HAS_PROC_NUM_THREADS = hasattr(cext, "proc_num_threads") -HAS_PROC_OPEN_FILES = hasattr(cext, 'proc_open_files') -HAS_PROC_NUM_FDS = hasattr(cext, 'proc_num_fds') - -kinfo_proc_map = dict( - ppid=0, - status=1, - real_uid=2, - effective_uid=3, - saved_uid=4, - real_gid=5, - effective_gid=6, - saved_gid=7, - ttynr=8, - create_time=9, - ctx_switches_vol=10, - ctx_switches_unvol=11, - read_io_count=12, - write_io_count=13, - user_time=14, - sys_time=15, - ch_user_time=16, - ch_sys_time=17, - rss=18, - vms=19, - memtext=20, - memdata=21, - memstack=22, - cpunum=23, - name=24, -) - - -# ===================================================================== -# --- named tuples -# ===================================================================== - - -# fmt: off -# psutil.virtual_memory() -svmem = namedtuple( - 'svmem', ['total', 'available', 'percent', 'used', 'free', - 'active', 'inactive', 'buffers', 'cached', 'shared', 'wired']) -# psutil.cpu_times() -scputimes = namedtuple( - 'scputimes', ['user', 'nice', 'system', 'idle', 'irq']) -# psutil.Process.memory_info() -pmem = namedtuple('pmem', ['rss', 'vms', 'text', 'data', 'stack']) -# psutil.Process.memory_full_info() -pfullmem = pmem -# psutil.Process.cpu_times() -pcputimes = namedtuple('pcputimes', - ['user', 'system', 'children_user', 'children_system']) -# psutil.Process.memory_maps(grouped=True) -pmmap_grouped = namedtuple( - 'pmmap_grouped', 'path rss, private, ref_count, shadow_count') -# psutil.Process.memory_maps(grouped=False) -pmmap_ext = namedtuple( - 'pmmap_ext', 'addr, perms path rss, private, ref_count, shadow_count') -# psutil.disk_io_counters() -if FREEBSD: - sdiskio = namedtuple('sdiskio', ['read_count', 'write_count', - 'read_bytes', 'write_bytes', - 'read_time', 'write_time', - 'busy_time']) -else: - sdiskio = namedtuple('sdiskio', ['read_count', 'write_count', - 'read_bytes', 'write_bytes']) -# fmt: on - - -# ===================================================================== -# --- memory -# ===================================================================== - - -def virtual_memory(): - mem = cext.virtual_mem() - if NETBSD: - total, free, active, inactive, wired, cached = mem - # On NetBSD buffers and shared mem is determined via /proc. - # The C ext set them to 0. - with open('/proc/meminfo', 'rb') as f: - for line in f: - if line.startswith(b'Buffers:'): - buffers = int(line.split()[1]) * 1024 - elif line.startswith(b'MemShared:'): - shared = int(line.split()[1]) * 1024 - # Before avail was calculated as (inactive + cached + free), - # same as zabbix, but it turned out it could exceed total (see - # #2233), so zabbix seems to be wrong. Htop calculates it - # differently, and the used value seem more realistic, so let's - # match htop. - # https://github.com/htop-dev/htop/blob/e7f447b/netbsd/NetBSDProcessList.c#L162 - # https://github.com/zabbix/zabbix/blob/af5e0f8/src/libs/zbxsysinfo/netbsd/memory.c#L135 - used = active + wired - avail = total - used - else: - total, free, active, inactive, wired, cached, buffers, shared = mem - # matches freebsd-memory CLI: - # * https://people.freebsd.org/~rse/dist/freebsd-memory - # * https://www.cyberciti.biz/files/scripts/freebsd-memory.pl.txt - # matches zabbix: - # * https://github.com/zabbix/zabbix/blob/af5e0f8/src/libs/zbxsysinfo/freebsd/memory.c#L143 - avail = inactive + cached + free - used = active + wired + cached - - percent = usage_percent((total - avail), total, round_=1) - return svmem( - total, - avail, - percent, - used, - free, - active, - inactive, - buffers, - cached, - shared, - wired, - ) - - -def swap_memory(): - """System swap memory as (total, used, free, sin, sout) namedtuple.""" - total, used, free, sin, sout = cext.swap_mem() - percent = usage_percent(used, total, round_=1) - return _common.sswap(total, used, free, percent, sin, sout) - - -# ===================================================================== -# --- CPU -# ===================================================================== - - -def cpu_times(): - """Return system per-CPU times as a namedtuple.""" - user, nice, system, idle, irq = cext.cpu_times() - return scputimes(user, nice, system, idle, irq) - - -if HAS_PER_CPU_TIMES: - - def per_cpu_times(): - """Return system CPU times as a namedtuple.""" - ret = [] - for cpu_t in cext.per_cpu_times(): - user, nice, system, idle, irq = cpu_t - item = scputimes(user, nice, system, idle, irq) - ret.append(item) - return ret - -else: - # XXX - # Ok, this is very dirty. - # On FreeBSD < 8 we cannot gather per-cpu information, see: - # https://github.com/giampaolo/psutil/issues/226 - # If num cpus > 1, on first call we return single cpu times to avoid a - # crash at psutil import time. - # Next calls will fail with NotImplementedError - def per_cpu_times(): - """Return system CPU times as a namedtuple.""" - if cpu_count_logical() == 1: - return [cpu_times()] - if per_cpu_times.__called__: - msg = "supported only starting from FreeBSD 8" - raise NotImplementedError(msg) - per_cpu_times.__called__ = True - return [cpu_times()] - - per_cpu_times.__called__ = False - - -def cpu_count_logical(): - """Return the number of logical CPUs in the system.""" - return cext.cpu_count_logical() - - -if OPENBSD or NETBSD: - - def cpu_count_cores(): - # OpenBSD and NetBSD do not implement this. - return 1 if cpu_count_logical() == 1 else None - -else: - - def cpu_count_cores(): - """Return the number of CPU cores in the system.""" - # From the C module we'll get an XML string similar to this: - # http://manpages.ubuntu.com/manpages/precise/man4/smp.4freebsd.html - # We may get None in case "sysctl kern.sched.topology_spec" - # is not supported on this BSD version, in which case we'll mimic - # os.cpu_count() and return None. - ret = None - s = cext.cpu_topology() - if s is not None: - # get rid of padding chars appended at the end of the string - index = s.rfind("") - if index != -1: - s = s[: index + 9] - root = ElementTree.fromstring(s) - try: - ret = len(root.findall('group/children/group/cpu')) or None - finally: - # needed otherwise it will memleak - root.clear() - if not ret: - # If logical CPUs == 1 it's obvious we' have only 1 core. - if cpu_count_logical() == 1: - return 1 - return ret - - -def cpu_stats(): - """Return various CPU stats as a named tuple.""" - if FREEBSD: - # Note: the C ext is returning some metrics we are not exposing: - # traps. - ctxsw, intrs, soft_intrs, syscalls, _traps = cext.cpu_stats() - elif NETBSD: - # XXX - # Note about intrs: the C extension returns 0. intrs - # can be determined via /proc/stat; it has the same value as - # soft_intrs thought so the kernel is faking it (?). - # - # Note about syscalls: the C extension always sets it to 0 (?). - # - # Note: the C ext is returning some metrics we are not exposing: - # traps, faults and forks. - ctxsw, intrs, soft_intrs, syscalls, _traps, _faults, _forks = ( - cext.cpu_stats() - ) - with open('/proc/stat', 'rb') as f: - for line in f: - if line.startswith(b'intr'): - intrs = int(line.split()[1]) - elif OPENBSD: - # Note: the C ext is returning some metrics we are not exposing: - # traps, faults and forks. - ctxsw, intrs, soft_intrs, syscalls, _traps, _faults, _forks = ( - cext.cpu_stats() - ) - return _common.scpustats(ctxsw, intrs, soft_intrs, syscalls) - - -if FREEBSD: - - def cpu_freq(): - """Return frequency metrics for CPUs. As of Dec 2018 only - CPU 0 appears to be supported by FreeBSD and all other cores - match the frequency of CPU 0. - """ - ret = [] - num_cpus = cpu_count_logical() - for cpu in range(num_cpus): - try: - current, available_freq = cext.cpu_freq(cpu) - except NotImplementedError: - continue - if available_freq: - try: - min_freq = int(available_freq.split(" ")[-1].split("/")[0]) - except (IndexError, ValueError): - min_freq = None - try: - max_freq = int(available_freq.split(" ")[0].split("/")[0]) - except (IndexError, ValueError): - max_freq = None - ret.append(_common.scpufreq(current, min_freq, max_freq)) - return ret - -elif OPENBSD: - - def cpu_freq(): - curr = float(cext.cpu_freq()) - return [_common.scpufreq(curr, 0.0, 0.0)] - - -# ===================================================================== -# --- disks -# ===================================================================== - - -def disk_partitions(all=False): - """Return mounted disk partitions as a list of namedtuples. - 'all' argument is ignored, see: - https://github.com/giampaolo/psutil/issues/906. - """ - retlist = [] - partitions = cext.disk_partitions() - for partition in partitions: - device, mountpoint, fstype, opts = partition - ntuple = _common.sdiskpart(device, mountpoint, fstype, opts) - retlist.append(ntuple) - return retlist - - -disk_usage = _psposix.disk_usage -disk_io_counters = cext.disk_io_counters - - -# ===================================================================== -# --- network -# ===================================================================== - - -net_io_counters = cext.net_io_counters -net_if_addrs = cext_posix.net_if_addrs - - -def net_if_stats(): - """Get NIC stats (isup, duplex, speed, mtu).""" - names = net_io_counters().keys() - ret = {} - for name in names: - try: - mtu = cext_posix.net_if_mtu(name) - flags = cext_posix.net_if_flags(name) - duplex, speed = cext_posix.net_if_duplex_speed(name) - except OSError as err: - # https://github.com/giampaolo/psutil/issues/1279 - if err.errno != errno.ENODEV: - raise - else: - if hasattr(_common, 'NicDuplex'): - duplex = _common.NicDuplex(duplex) - output_flags = ','.join(flags) - isup = 'running' in flags - ret[name] = _common.snicstats( - isup, duplex, speed, mtu, output_flags - ) - return ret - - -def net_connections(kind): - """System-wide network connections.""" - families, types = conn_tmap[kind] - ret = set() - if OPENBSD: - rawlist = cext.net_connections(-1, families, types) - elif NETBSD: - rawlist = cext.net_connections(-1, kind) - else: # FreeBSD - rawlist = cext.net_connections(families, types) - - for item in rawlist: - fd, fam, type, laddr, raddr, status, pid = item - nt = conn_to_ntuple( - fd, fam, type, laddr, raddr, status, TCP_STATUSES, pid - ) - ret.add(nt) - return list(ret) - - -# ===================================================================== -# --- sensors -# ===================================================================== - - -if FREEBSD: - - def sensors_battery(): - """Return battery info.""" - try: - percent, minsleft, power_plugged = cext.sensors_battery() - except NotImplementedError: - # See: https://github.com/giampaolo/psutil/issues/1074 - return None - power_plugged = power_plugged == 1 - if power_plugged: - secsleft = _common.POWER_TIME_UNLIMITED - elif minsleft == -1: - secsleft = _common.POWER_TIME_UNKNOWN - else: - secsleft = minsleft * 60 - return _common.sbattery(percent, secsleft, power_plugged) - - def sensors_temperatures(): - """Return CPU cores temperatures if available, else an empty dict.""" - ret = defaultdict(list) - num_cpus = cpu_count_logical() - for cpu in range(num_cpus): - try: - current, high = cext.sensors_cpu_temperature(cpu) - if high <= 0: - high = None - name = f"Core {cpu}" - ret["coretemp"].append( - _common.shwtemp(name, current, high, high) - ) - except NotImplementedError: - pass - - return ret - - -# ===================================================================== -# --- other system functions -# ===================================================================== - - -def boot_time(): - """The system boot time expressed in seconds since the epoch.""" - return cext.boot_time() - - -def users(): - """Return currently connected users as a list of namedtuples.""" - retlist = [] - rawlist = cext.users() - for item in rawlist: - user, tty, hostname, tstamp, pid = item - if pid == -1: - assert OPENBSD - pid = None - if tty == '~': - continue # reboot or shutdown - nt = _common.suser(user, tty or None, hostname, tstamp, pid) - retlist.append(nt) - return retlist - - -# ===================================================================== -# --- processes -# ===================================================================== - - -@memoize -def _pid_0_exists(): - try: - Process(0).name() - except NoSuchProcess: - return False - except AccessDenied: - return True - else: - return True - - -def pids(): - """Returns a list of PIDs currently running on the system.""" - ret = cext.pids() - if OPENBSD and (0 not in ret) and _pid_0_exists(): - # On OpenBSD the kernel does not return PID 0 (neither does - # ps) but it's actually querable (Process(0) will succeed). - ret.insert(0, 0) - return ret - - -if NETBSD: - - def pid_exists(pid): - exists = _psposix.pid_exists(pid) - if not exists: - # We do this because _psposix.pid_exists() lies in case of - # zombie processes. - return pid in pids() - else: - return True - -elif OPENBSD: - - def pid_exists(pid): - exists = _psposix.pid_exists(pid) - if not exists: - return False - else: - # OpenBSD seems to be the only BSD platform where - # _psposix.pid_exists() returns True for thread IDs (tids), - # so we can't use it. - return pid in pids() - -else: # FreeBSD - pid_exists = _psposix.pid_exists - - -def is_zombie(pid): - try: - st = cext.proc_oneshot_info(pid)[kinfo_proc_map['status']] - return PROC_STATUSES.get(st) == _common.STATUS_ZOMBIE - except OSError: - return False - - -def wrap_exceptions(fun): - """Decorator which translates bare OSError exceptions into - NoSuchProcess and AccessDenied. - """ - - @functools.wraps(fun) - def wrapper(self, *args, **kwargs): - pid, ppid, name = self.pid, self._ppid, self._name - try: - return fun(self, *args, **kwargs) - except ProcessLookupError as err: - if is_zombie(pid): - raise ZombieProcess(pid, name, ppid) from err - raise NoSuchProcess(pid, name) from err - except PermissionError as err: - raise AccessDenied(pid, name) from err - except OSError as err: - if pid == 0 and 0 in pids(): - raise AccessDenied(pid, name) from err - raise - - return wrapper - - -@contextlib.contextmanager -def wrap_exceptions_procfs(inst): - """Same as above, for routines relying on reading /proc fs.""" - pid, name, ppid = inst.pid, inst._name, inst._ppid - try: - yield - except (ProcessLookupError, FileNotFoundError) as err: - # ENOENT (no such file or directory) gets raised on open(). - # ESRCH (no such process) can get raised on read() if - # process is gone in meantime. - if is_zombie(inst.pid): - raise ZombieProcess(pid, name, ppid) from err - else: - raise NoSuchProcess(pid, name) from err - except PermissionError as err: - raise AccessDenied(pid, name) from err - - -class Process: - """Wrapper class around underlying C implementation.""" - - __slots__ = ["_cache", "_name", "_ppid", "pid"] - - def __init__(self, pid): - self.pid = pid - self._name = None - self._ppid = None - - def _assert_alive(self): - """Raise NSP if the process disappeared on us.""" - # For those C function who do not raise NSP, possibly returning - # incorrect or incomplete result. - cext.proc_name(self.pid) - - @wrap_exceptions - @memoize_when_activated - def oneshot(self): - """Retrieves multiple process info in one shot as a raw tuple.""" - ret = cext.proc_oneshot_info(self.pid) - assert len(ret) == len(kinfo_proc_map) - return ret - - def oneshot_enter(self): - self.oneshot.cache_activate(self) - - def oneshot_exit(self): - self.oneshot.cache_deactivate(self) - - @wrap_exceptions - def name(self): - name = self.oneshot()[kinfo_proc_map['name']] - return name if name is not None else cext.proc_name(self.pid) - - @wrap_exceptions - def exe(self): - if FREEBSD: - if self.pid == 0: - return '' # else NSP - return cext.proc_exe(self.pid) - elif NETBSD: - if self.pid == 0: - # /proc/0 dir exists but /proc/0/exe doesn't - return "" - with wrap_exceptions_procfs(self): - return os.readlink(f"/proc/{self.pid}/exe") - else: - # OpenBSD: exe cannot be determined; references: - # https://chromium.googlesource.com/chromium/src/base/+/ - # master/base_paths_posix.cc - # We try our best guess by using which against the first - # cmdline arg (may return None). - import shutil - - cmdline = self.cmdline() - if cmdline: - return shutil.which(cmdline[0]) or "" - else: - return "" - - @wrap_exceptions - def cmdline(self): - if OPENBSD and self.pid == 0: - return [] # ...else it crashes - elif NETBSD: - # XXX - most of the times the underlying sysctl() call on - # NetBSD and OpenBSD returns a truncated string. Also - # /proc/pid/cmdline behaves the same so it looks like this - # is a kernel bug. - try: - return cext.proc_cmdline(self.pid) - except OSError as err: - if err.errno == errno.EINVAL: - pid, name, ppid = self.pid, self._name, self._ppid - if is_zombie(self.pid): - raise ZombieProcess(pid, name, ppid) from err - if not pid_exists(self.pid): - raise NoSuchProcess(pid, name, ppid) from err - # XXX: this happens with unicode tests. It means the C - # routine is unable to decode invalid unicode chars. - debug(f"ignoring {err!r} and returning an empty list") - return [] - else: - raise - else: - return cext.proc_cmdline(self.pid) - - @wrap_exceptions - def environ(self): - return cext.proc_environ(self.pid) - - @wrap_exceptions - def terminal(self): - tty_nr = self.oneshot()[kinfo_proc_map['ttynr']] - tmap = _psposix.get_terminal_map() - try: - return tmap[tty_nr] - except KeyError: - return None - - @wrap_exceptions - def ppid(self): - self._ppid = self.oneshot()[kinfo_proc_map['ppid']] - return self._ppid - - @wrap_exceptions - def uids(self): - rawtuple = self.oneshot() - return _common.puids( - rawtuple[kinfo_proc_map['real_uid']], - rawtuple[kinfo_proc_map['effective_uid']], - rawtuple[kinfo_proc_map['saved_uid']], - ) - - @wrap_exceptions - def gids(self): - rawtuple = self.oneshot() - return _common.pgids( - rawtuple[kinfo_proc_map['real_gid']], - rawtuple[kinfo_proc_map['effective_gid']], - rawtuple[kinfo_proc_map['saved_gid']], - ) - - @wrap_exceptions - def cpu_times(self): - rawtuple = self.oneshot() - return _common.pcputimes( - rawtuple[kinfo_proc_map['user_time']], - rawtuple[kinfo_proc_map['sys_time']], - rawtuple[kinfo_proc_map['ch_user_time']], - rawtuple[kinfo_proc_map['ch_sys_time']], - ) - - if FREEBSD: - - @wrap_exceptions - def cpu_num(self): - return self.oneshot()[kinfo_proc_map['cpunum']] - - @wrap_exceptions - def memory_info(self): - rawtuple = self.oneshot() - return pmem( - rawtuple[kinfo_proc_map['rss']], - rawtuple[kinfo_proc_map['vms']], - rawtuple[kinfo_proc_map['memtext']], - rawtuple[kinfo_proc_map['memdata']], - rawtuple[kinfo_proc_map['memstack']], - ) - - memory_full_info = memory_info - - @wrap_exceptions - def create_time(self): - return self.oneshot()[kinfo_proc_map['create_time']] - - @wrap_exceptions - def num_threads(self): - if HAS_PROC_NUM_THREADS: - # FreeBSD - return cext.proc_num_threads(self.pid) - else: - return len(self.threads()) - - @wrap_exceptions - def num_ctx_switches(self): - rawtuple = self.oneshot() - return _common.pctxsw( - rawtuple[kinfo_proc_map['ctx_switches_vol']], - rawtuple[kinfo_proc_map['ctx_switches_unvol']], - ) - - @wrap_exceptions - def threads(self): - # Note: on OpenSBD this (/dev/mem) requires root access. - rawlist = cext.proc_threads(self.pid) - retlist = [] - for thread_id, utime, stime in rawlist: - ntuple = _common.pthread(thread_id, utime, stime) - retlist.append(ntuple) - if OPENBSD: - self._assert_alive() - return retlist - - @wrap_exceptions - def net_connections(self, kind='inet'): - families, types = conn_tmap[kind] - ret = [] - - if NETBSD: - rawlist = cext.net_connections(self.pid, kind) - elif OPENBSD: - rawlist = cext.net_connections(self.pid, families, types) - else: - rawlist = cext.proc_net_connections(self.pid, families, types) - - for item in rawlist: - fd, fam, type, laddr, raddr, status = item[:6] - if FREEBSD: - if (fam not in families) or (type not in types): - continue - nt = conn_to_ntuple( - fd, fam, type, laddr, raddr, status, TCP_STATUSES - ) - ret.append(nt) - - self._assert_alive() - return ret - - @wrap_exceptions - def wait(self, timeout=None): - return _psposix.wait_pid(self.pid, timeout, self._name) - - @wrap_exceptions - def nice_get(self): - return cext_posix.getpriority(self.pid) - - @wrap_exceptions - def nice_set(self, value): - return cext_posix.setpriority(self.pid, value) - - @wrap_exceptions - def status(self): - code = self.oneshot()[kinfo_proc_map['status']] - # XXX is '?' legit? (we're not supposed to return it anyway) - return PROC_STATUSES.get(code, '?') - - @wrap_exceptions - def io_counters(self): - rawtuple = self.oneshot() - return _common.pio( - rawtuple[kinfo_proc_map['read_io_count']], - rawtuple[kinfo_proc_map['write_io_count']], - -1, - -1, - ) - - @wrap_exceptions - def cwd(self): - """Return process current working directory.""" - # sometimes we get an empty string, in which case we turn - # it into None - if OPENBSD and self.pid == 0: - return "" # ...else it would raise EINVAL - elif NETBSD or HAS_PROC_OPEN_FILES: - # FreeBSD < 8 does not support functions based on - # kinfo_getfile() and kinfo_getvmmap() - return cext.proc_cwd(self.pid) - else: - raise NotImplementedError( - "supported only starting from FreeBSD 8" if FREEBSD else "" - ) - - nt_mmap_grouped = namedtuple( - 'mmap', 'path rss, private, ref_count, shadow_count' - ) - nt_mmap_ext = namedtuple( - 'mmap', 'addr, perms path rss, private, ref_count, shadow_count' - ) - - def _not_implemented(self): - raise NotImplementedError - - # FreeBSD < 8 does not support functions based on kinfo_getfile() - # and kinfo_getvmmap() - if HAS_PROC_OPEN_FILES: - - @wrap_exceptions - def open_files(self): - """Return files opened by process as a list of namedtuples.""" - rawlist = cext.proc_open_files(self.pid) - return [_common.popenfile(path, fd) for path, fd in rawlist] - - else: - open_files = _not_implemented - - # FreeBSD < 8 does not support functions based on kinfo_getfile() - # and kinfo_getvmmap() - if HAS_PROC_NUM_FDS: - - @wrap_exceptions - def num_fds(self): - """Return the number of file descriptors opened by this process.""" - ret = cext.proc_num_fds(self.pid) - if NETBSD: - self._assert_alive() - return ret - - else: - num_fds = _not_implemented - - # --- FreeBSD only APIs - - if FREEBSD: - - @wrap_exceptions - def cpu_affinity_get(self): - return cext.proc_cpu_affinity_get(self.pid) - - @wrap_exceptions - def cpu_affinity_set(self, cpus): - # Pre-emptively check if CPUs are valid because the C - # function has a weird behavior in case of invalid CPUs, - # see: https://github.com/giampaolo/psutil/issues/586 - allcpus = set(range(len(per_cpu_times()))) - for cpu in cpus: - if cpu not in allcpus: - msg = f"invalid CPU {cpu!r} (choose between {allcpus})" - raise ValueError(msg) - try: - cext.proc_cpu_affinity_set(self.pid, cpus) - except OSError as err: - # 'man cpuset_setaffinity' about EDEADLK: - # <> - if err.errno in {errno.EINVAL, errno.EDEADLK}: - for cpu in cpus: - if cpu not in allcpus: - msg = ( - f"invalid CPU {cpu!r} (choose between" - f" {allcpus})" - ) - raise ValueError(msg) from err - raise - - @wrap_exceptions - def memory_maps(self): - return cext.proc_memory_maps(self.pid) - - @wrap_exceptions - def rlimit(self, resource, limits=None): - if limits is None: - return cext.proc_getrlimit(self.pid, resource) - else: - if len(limits) != 2: - msg = ( - "second argument must be a (soft, hard) tuple, got" - f" {limits!r}" - ) - raise ValueError(msg) - soft, hard = limits - return cext.proc_setrlimit(self.pid, resource, soft, hard) diff --git a/PortablePython/Lib/site-packages/psutil/_pslinux.py b/PortablePython/Lib/site-packages/psutil/_pslinux.py deleted file mode 100644 index 8cc64e9..0000000 --- a/PortablePython/Lib/site-packages/psutil/_pslinux.py +++ /dev/null @@ -1,2295 +0,0 @@ -# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -"""Linux platform implementation.""" - - -import base64 -import collections -import enum -import errno -import functools -import glob -import os -import re -import resource -import socket -import struct -import sys -import warnings -from collections import defaultdict -from collections import namedtuple - -from . import _common -from . import _psposix -from . import _psutil_linux as cext -from . import _psutil_posix as cext_posix -from ._common import ENCODING -from ._common import NIC_DUPLEX_FULL -from ._common import NIC_DUPLEX_HALF -from ._common import NIC_DUPLEX_UNKNOWN -from ._common import AccessDenied -from ._common import NoSuchProcess -from ._common import ZombieProcess -from ._common import bcat -from ._common import cat -from ._common import debug -from ._common import decode -from ._common import get_procfs_path -from ._common import isfile_strict -from ._common import memoize -from ._common import memoize_when_activated -from ._common import open_binary -from ._common import open_text -from ._common import parse_environ_block -from ._common import path_exists_strict -from ._common import supports_ipv6 -from ._common import usage_percent - - -# fmt: off -__extra__all__ = [ - 'PROCFS_PATH', - # io prio constants - "IOPRIO_CLASS_NONE", "IOPRIO_CLASS_RT", "IOPRIO_CLASS_BE", - "IOPRIO_CLASS_IDLE", - # connection status constants - "CONN_ESTABLISHED", "CONN_SYN_SENT", "CONN_SYN_RECV", "CONN_FIN_WAIT1", - "CONN_FIN_WAIT2", "CONN_TIME_WAIT", "CONN_CLOSE", "CONN_CLOSE_WAIT", - "CONN_LAST_ACK", "CONN_LISTEN", "CONN_CLOSING", -] - -if hasattr(resource, "prlimit"): - __extra__all__.extend( - [x for x in dir(cext) if x.startswith('RLIM') and x.isupper()] - ) -# fmt: on - - -# ===================================================================== -# --- globals -# ===================================================================== - - -POWER_SUPPLY_PATH = "/sys/class/power_supply" -HAS_PROC_SMAPS = os.path.exists(f"/proc/{os.getpid()}/smaps") -HAS_PROC_SMAPS_ROLLUP = os.path.exists(f"/proc/{os.getpid()}/smaps_rollup") -HAS_PROC_IO_PRIORITY = hasattr(cext, "proc_ioprio_get") -HAS_CPU_AFFINITY = hasattr(cext, "proc_cpu_affinity_get") - -# Number of clock ticks per second -CLOCK_TICKS = os.sysconf("SC_CLK_TCK") -PAGESIZE = cext_posix.getpagesize() -BOOT_TIME = None # set later -LITTLE_ENDIAN = sys.byteorder == 'little' - -# "man iostat" states that sectors are equivalent with blocks and have -# a size of 512 bytes. Despite this value can be queried at runtime -# via /sys/block/{DISK}/queue/hw_sector_size and results may vary -# between 1k, 2k, or 4k... 512 appears to be a magic constant used -# throughout Linux source code: -# * https://stackoverflow.com/a/38136179/376587 -# * https://lists.gt.net/linux/kernel/2241060 -# * https://github.com/giampaolo/psutil/issues/1305 -# * https://github.com/torvalds/linux/blob/ -# 4f671fe2f9523a1ea206f63fe60a7c7b3a56d5c7/include/linux/bio.h#L99 -# * https://lkml.org/lkml/2015/8/17/234 -DISK_SECTOR_SIZE = 512 - -AddressFamily = enum.IntEnum( - 'AddressFamily', {'AF_LINK': int(socket.AF_PACKET)} -) -AF_LINK = AddressFamily.AF_LINK - - -# ioprio_* constants http://linux.die.net/man/2/ioprio_get -class IOPriority(enum.IntEnum): - IOPRIO_CLASS_NONE = 0 - IOPRIO_CLASS_RT = 1 - IOPRIO_CLASS_BE = 2 - IOPRIO_CLASS_IDLE = 3 - - -globals().update(IOPriority.__members__) - -# See: -# https://github.com/torvalds/linux/blame/master/fs/proc/array.c -# ...and (TASK_* constants): -# https://github.com/torvalds/linux/blob/master/include/linux/sched.h -PROC_STATUSES = { - "R": _common.STATUS_RUNNING, - "S": _common.STATUS_SLEEPING, - "D": _common.STATUS_DISK_SLEEP, - "T": _common.STATUS_STOPPED, - "t": _common.STATUS_TRACING_STOP, - "Z": _common.STATUS_ZOMBIE, - "X": _common.STATUS_DEAD, - "x": _common.STATUS_DEAD, - "K": _common.STATUS_WAKE_KILL, - "W": _common.STATUS_WAKING, - "I": _common.STATUS_IDLE, - "P": _common.STATUS_PARKED, -} - -# https://github.com/torvalds/linux/blob/master/include/net/tcp_states.h -TCP_STATUSES = { - "01": _common.CONN_ESTABLISHED, - "02": _common.CONN_SYN_SENT, - "03": _common.CONN_SYN_RECV, - "04": _common.CONN_FIN_WAIT1, - "05": _common.CONN_FIN_WAIT2, - "06": _common.CONN_TIME_WAIT, - "07": _common.CONN_CLOSE, - "08": _common.CONN_CLOSE_WAIT, - "09": _common.CONN_LAST_ACK, - "0A": _common.CONN_LISTEN, - "0B": _common.CONN_CLOSING, -} - - -# ===================================================================== -# --- named tuples -# ===================================================================== - - -# fmt: off -# psutil.virtual_memory() -svmem = namedtuple( - 'svmem', ['total', 'available', 'percent', 'used', 'free', - 'active', 'inactive', 'buffers', 'cached', 'shared', 'slab']) -# psutil.disk_io_counters() -sdiskio = namedtuple( - 'sdiskio', ['read_count', 'write_count', - 'read_bytes', 'write_bytes', - 'read_time', 'write_time', - 'read_merged_count', 'write_merged_count', - 'busy_time']) -# psutil.Process().open_files() -popenfile = namedtuple( - 'popenfile', ['path', 'fd', 'position', 'mode', 'flags']) -# psutil.Process().memory_info() -pmem = namedtuple('pmem', 'rss vms shared text lib data dirty') -# psutil.Process().memory_full_info() -pfullmem = namedtuple('pfullmem', pmem._fields + ('uss', 'pss', 'swap')) -# psutil.Process().memory_maps(grouped=True) -pmmap_grouped = namedtuple( - 'pmmap_grouped', - ['path', 'rss', 'size', 'pss', 'shared_clean', 'shared_dirty', - 'private_clean', 'private_dirty', 'referenced', 'anonymous', 'swap']) -# psutil.Process().memory_maps(grouped=False) -pmmap_ext = namedtuple( - 'pmmap_ext', 'addr perms ' + ' '.join(pmmap_grouped._fields)) -# psutil.Process.io_counters() -pio = namedtuple('pio', ['read_count', 'write_count', - 'read_bytes', 'write_bytes', - 'read_chars', 'write_chars']) -# psutil.Process.cpu_times() -pcputimes = namedtuple('pcputimes', - ['user', 'system', 'children_user', 'children_system', - 'iowait']) -# fmt: on - - -# ===================================================================== -# --- utils -# ===================================================================== - - -def readlink(path): - """Wrapper around os.readlink().""" - assert isinstance(path, str), path - path = os.readlink(path) - # readlink() might return paths containing null bytes ('\x00') - # resulting in "TypeError: must be encoded string without NULL - # bytes, not str" errors when the string is passed to other - # fs-related functions (os.*, open(), ...). - # Apparently everything after '\x00' is garbage (we can have - # ' (deleted)', 'new' and possibly others), see: - # https://github.com/giampaolo/psutil/issues/717 - path = path.split('\x00')[0] - # Certain paths have ' (deleted)' appended. Usually this is - # bogus as the file actually exists. Even if it doesn't we - # don't care. - if path.endswith(' (deleted)') and not path_exists_strict(path): - path = path[:-10] - return path - - -def file_flags_to_mode(flags): - """Convert file's open() flags into a readable string. - Used by Process.open_files(). - """ - modes_map = {os.O_RDONLY: 'r', os.O_WRONLY: 'w', os.O_RDWR: 'w+'} - mode = modes_map[flags & (os.O_RDONLY | os.O_WRONLY | os.O_RDWR)] - if flags & os.O_APPEND: - mode = mode.replace('w', 'a', 1) - mode = mode.replace('w+', 'r+') - # possible values: r, w, a, r+, a+ - return mode - - -def is_storage_device(name): - """Return True if the given name refers to a root device (e.g. - "sda", "nvme0n1") as opposed to a logical partition (e.g. "sda1", - "nvme0n1p1"). If name is a virtual device (e.g. "loop1", "ram") - return True. - """ - # Re-adapted from iostat source code, see: - # https://github.com/sysstat/sysstat/blob/ - # 97912938cd476645b267280069e83b1c8dc0e1c7/common.c#L208 - # Some devices may have a slash in their name (e.g. cciss/c0d0...). - name = name.replace('/', '!') - including_virtual = True - if including_virtual: - path = f"/sys/block/{name}" - else: - path = f"/sys/block/{name}/device" - return os.access(path, os.F_OK) - - -@memoize -def set_scputimes_ntuple(procfs_path): - """Set a namedtuple of variable fields depending on the CPU times - available on this Linux kernel version which may be: - (user, nice, system, idle, iowait, irq, softirq, [steal, [guest, - [guest_nice]]]) - Used by cpu_times() function. - """ - global scputimes - with open_binary(f"{procfs_path}/stat") as f: - values = f.readline().split()[1:] - fields = ['user', 'nice', 'system', 'idle', 'iowait', 'irq', 'softirq'] - vlen = len(values) - if vlen >= 8: - # Linux >= 2.6.11 - fields.append('steal') - if vlen >= 9: - # Linux >= 2.6.24 - fields.append('guest') - if vlen >= 10: - # Linux >= 3.2.0 - fields.append('guest_nice') - scputimes = namedtuple('scputimes', fields) - - -try: - set_scputimes_ntuple("/proc") -except Exception as err: # noqa: BLE001 - # Don't want to crash at import time. - debug(f"ignoring exception on import: {err!r}") - scputimes = namedtuple('scputimes', 'user system idle')(0.0, 0.0, 0.0) - - -# ===================================================================== -# --- system memory -# ===================================================================== - - -def calculate_avail_vmem(mems): - """Fallback for kernels < 3.14 where /proc/meminfo does not provide - "MemAvailable", see: - https://blog.famzah.net/2014/09/24/. - - This code reimplements the algorithm outlined here: - https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/ - commit/?id=34e431b0ae398fc54ea69ff85ec700722c9da773 - - We use this function also when "MemAvailable" returns 0 (possibly a - kernel bug, see: https://github.com/giampaolo/psutil/issues/1915). - In that case this routine matches "free" CLI tool result ("available" - column). - - XXX: on recent kernels this calculation may differ by ~1.5% compared - to "MemAvailable:", as it's calculated slightly differently. - It is still way more realistic than doing (free + cached) though. - See: - * https://gitlab.com/procps-ng/procps/issues/42 - * https://github.com/famzah/linux-memavailable-procfs/issues/2 - """ - # Note about "fallback" value. According to: - # https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/ - # commit/?id=34e431b0ae398fc54ea69ff85ec700722c9da773 - # ...long ago "available" memory was calculated as (free + cached), - # We use fallback when one of these is missing from /proc/meminfo: - # "Active(file)": introduced in 2.6.28 / Dec 2008 - # "Inactive(file)": introduced in 2.6.28 / Dec 2008 - # "SReclaimable": introduced in 2.6.19 / Nov 2006 - # /proc/zoneinfo: introduced in 2.6.13 / Aug 2005 - free = mems[b'MemFree:'] - fallback = free + mems.get(b"Cached:", 0) - try: - lru_active_file = mems[b'Active(file):'] - lru_inactive_file = mems[b'Inactive(file):'] - slab_reclaimable = mems[b'SReclaimable:'] - except KeyError as err: - debug( - f"{err.args[0]} is missing from /proc/meminfo; using an" - " approximation for calculating available memory" - ) - return fallback - try: - f = open_binary(f"{get_procfs_path()}/zoneinfo") - except OSError: - return fallback # kernel 2.6.13 - - watermark_low = 0 - with f: - for line in f: - line = line.strip() - if line.startswith(b'low'): - watermark_low += int(line.split()[1]) - watermark_low *= PAGESIZE - - avail = free - watermark_low - pagecache = lru_active_file + lru_inactive_file - pagecache -= min(pagecache / 2, watermark_low) - avail += pagecache - avail += slab_reclaimable - min(slab_reclaimable / 2.0, watermark_low) - return int(avail) - - -def virtual_memory(): - """Report virtual memory stats. - This implementation mimics procps-ng-3.3.12, aka "free" CLI tool: - https://gitlab.com/procps-ng/procps/blob/ - 24fd2605c51fccc375ab0287cec33aa767f06718/proc/sysinfo.c#L778-791 - The returned values are supposed to match both "free" and "vmstat -s" - CLI tools. - """ - missing_fields = [] - mems = {} - with open_binary(f"{get_procfs_path()}/meminfo") as f: - for line in f: - fields = line.split() - mems[fields[0]] = int(fields[1]) * 1024 - - # /proc doc states that the available fields in /proc/meminfo vary - # by architecture and compile options, but these 3 values are also - # returned by sysinfo(2); as such we assume they are always there. - total = mems[b'MemTotal:'] - free = mems[b'MemFree:'] - try: - buffers = mems[b'Buffers:'] - except KeyError: - # https://github.com/giampaolo/psutil/issues/1010 - buffers = 0 - missing_fields.append('buffers') - try: - cached = mems[b"Cached:"] - except KeyError: - cached = 0 - missing_fields.append('cached') - else: - # "free" cmdline utility sums reclaimable to cached. - # Older versions of procps used to add slab memory instead. - # This got changed in: - # https://gitlab.com/procps-ng/procps/commit/ - # 05d751c4f076a2f0118b914c5e51cfbb4762ad8e - cached += mems.get(b"SReclaimable:", 0) # since kernel 2.6.19 - - try: - shared = mems[b'Shmem:'] # since kernel 2.6.32 - except KeyError: - try: - shared = mems[b'MemShared:'] # kernels 2.4 - except KeyError: - shared = 0 - missing_fields.append('shared') - - try: - active = mems[b"Active:"] - except KeyError: - active = 0 - missing_fields.append('active') - - try: - inactive = mems[b"Inactive:"] - except KeyError: - try: - inactive = ( - mems[b"Inact_dirty:"] - + mems[b"Inact_clean:"] - + mems[b"Inact_laundry:"] - ) - except KeyError: - inactive = 0 - missing_fields.append('inactive') - - try: - slab = mems[b"Slab:"] - except KeyError: - slab = 0 - - used = total - free - cached - buffers - if used < 0: - # May be symptomatic of running within a LCX container where such - # values will be dramatically distorted over those of the host. - used = total - free - - # - starting from 4.4.0 we match free's "available" column. - # Before 4.4.0 we calculated it as (free + buffers + cached) - # which matched htop. - # - free and htop available memory differs as per: - # http://askubuntu.com/a/369589 - # http://unix.stackexchange.com/a/65852/168884 - # - MemAvailable has been introduced in kernel 3.14 - try: - avail = mems[b'MemAvailable:'] - except KeyError: - avail = calculate_avail_vmem(mems) - else: - if avail == 0: - # Yes, it can happen (probably a kernel bug): - # https://github.com/giampaolo/psutil/issues/1915 - # In this case "free" CLI tool makes an estimate. We do the same, - # and it matches "free" CLI tool. - avail = calculate_avail_vmem(mems) - - if avail < 0: - avail = 0 - missing_fields.append('available') - elif avail > total: - # If avail is greater than total or our calculation overflows, - # that's symptomatic of running within a LCX container where such - # values will be dramatically distorted over those of the host. - # https://gitlab.com/procps-ng/procps/blob/ - # 24fd2605c51fccc375ab0287cec33aa767f06718/proc/sysinfo.c#L764 - avail = free - - percent = usage_percent((total - avail), total, round_=1) - - # Warn about missing metrics which are set to 0. - if missing_fields: - msg = "{} memory stats couldn't be determined and {} set to 0".format( - ", ".join(missing_fields), - "was" if len(missing_fields) == 1 else "were", - ) - warnings.warn(msg, RuntimeWarning, stacklevel=2) - - return svmem( - total, - avail, - percent, - used, - free, - active, - inactive, - buffers, - cached, - shared, - slab, - ) - - -def swap_memory(): - """Return swap memory metrics.""" - mems = {} - with open_binary(f"{get_procfs_path()}/meminfo") as f: - for line in f: - fields = line.split() - mems[fields[0]] = int(fields[1]) * 1024 - # We prefer /proc/meminfo over sysinfo() syscall so that - # psutil.PROCFS_PATH can be used in order to allow retrieval - # for linux containers, see: - # https://github.com/giampaolo/psutil/issues/1015 - try: - total = mems[b'SwapTotal:'] - free = mems[b'SwapFree:'] - except KeyError: - _, _, _, _, total, free, unit_multiplier = cext.linux_sysinfo() - total *= unit_multiplier - free *= unit_multiplier - - used = total - free - percent = usage_percent(used, total, round_=1) - # get pgin/pgouts - try: - f = open_binary(f"{get_procfs_path()}/vmstat") - except OSError as err: - # see https://github.com/giampaolo/psutil/issues/722 - msg = ( - "'sin' and 'sout' swap memory stats couldn't " - f"be determined and were set to 0 ({err})" - ) - warnings.warn(msg, RuntimeWarning, stacklevel=2) - sin = sout = 0 - else: - with f: - sin = sout = None - for line in f: - # values are expressed in 4 kilo bytes, we want - # bytes instead - if line.startswith(b'pswpin'): - sin = int(line.split(b' ')[1]) * 4 * 1024 - elif line.startswith(b'pswpout'): - sout = int(line.split(b' ')[1]) * 4 * 1024 - if sin is not None and sout is not None: - break - else: - # we might get here when dealing with exotic Linux - # flavors, see: - # https://github.com/giampaolo/psutil/issues/313 - msg = "'sin' and 'sout' swap memory stats couldn't " - msg += "be determined and were set to 0" - warnings.warn(msg, RuntimeWarning, stacklevel=2) - sin = sout = 0 - return _common.sswap(total, used, free, percent, sin, sout) - - -# ===================================================================== -# --- CPU -# ===================================================================== - - -def cpu_times(): - """Return a named tuple representing the following system-wide - CPU times: - (user, nice, system, idle, iowait, irq, softirq [steal, [guest, - [guest_nice]]]) - Last 3 fields may not be available on all Linux kernel versions. - """ - procfs_path = get_procfs_path() - set_scputimes_ntuple(procfs_path) - with open_binary(f"{procfs_path}/stat") as f: - values = f.readline().split() - fields = values[1 : len(scputimes._fields) + 1] - fields = [float(x) / CLOCK_TICKS for x in fields] - return scputimes(*fields) - - -def per_cpu_times(): - """Return a list of namedtuple representing the CPU times - for every CPU available on the system. - """ - procfs_path = get_procfs_path() - set_scputimes_ntuple(procfs_path) - cpus = [] - with open_binary(f"{procfs_path}/stat") as f: - # get rid of the first line which refers to system wide CPU stats - f.readline() - for line in f: - if line.startswith(b'cpu'): - values = line.split() - fields = values[1 : len(scputimes._fields) + 1] - fields = [float(x) / CLOCK_TICKS for x in fields] - entry = scputimes(*fields) - cpus.append(entry) - return cpus - - -def cpu_count_logical(): - """Return the number of logical CPUs in the system.""" - try: - return os.sysconf("SC_NPROCESSORS_ONLN") - except ValueError: - # as a second fallback we try to parse /proc/cpuinfo - num = 0 - with open_binary(f"{get_procfs_path()}/cpuinfo") as f: - for line in f: - if line.lower().startswith(b'processor'): - num += 1 - - # unknown format (e.g. amrel/sparc architectures), see: - # https://github.com/giampaolo/psutil/issues/200 - # try to parse /proc/stat as a last resort - if num == 0: - search = re.compile(r'cpu\d') - with open_text(f"{get_procfs_path()}/stat") as f: - for line in f: - line = line.split(' ')[0] - if search.match(line): - num += 1 - - if num == 0: - # mimic os.cpu_count() - return None - return num - - -def cpu_count_cores(): - """Return the number of CPU cores in the system.""" - # Method #1 - ls = set() - # These 2 files are the same but */core_cpus_list is newer while - # */thread_siblings_list is deprecated and may disappear in the future. - # https://www.kernel.org/doc/Documentation/admin-guide/cputopology.rst - # https://github.com/giampaolo/psutil/pull/1727#issuecomment-707624964 - # https://lkml.org/lkml/2019/2/26/41 - p1 = "/sys/devices/system/cpu/cpu[0-9]*/topology/core_cpus_list" - p2 = "/sys/devices/system/cpu/cpu[0-9]*/topology/thread_siblings_list" - for path in glob.glob(p1) or glob.glob(p2): - with open_binary(path) as f: - ls.add(f.read().strip()) - result = len(ls) - if result != 0: - return result - - # Method #2 - mapping = {} - current_info = {} - with open_binary(f"{get_procfs_path()}/cpuinfo") as f: - for line in f: - line = line.strip().lower() - if not line: - # new section - try: - mapping[current_info[b'physical id']] = current_info[ - b'cpu cores' - ] - except KeyError: - pass - current_info = {} - elif line.startswith((b'physical id', b'cpu cores')): - # ongoing section - key, value = line.split(b'\t:', 1) - current_info[key] = int(value) - - result = sum(mapping.values()) - return result or None # mimic os.cpu_count() - - -def cpu_stats(): - """Return various CPU stats as a named tuple.""" - with open_binary(f"{get_procfs_path()}/stat") as f: - ctx_switches = None - interrupts = None - soft_interrupts = None - for line in f: - if line.startswith(b'ctxt'): - ctx_switches = int(line.split()[1]) - elif line.startswith(b'intr'): - interrupts = int(line.split()[1]) - elif line.startswith(b'softirq'): - soft_interrupts = int(line.split()[1]) - if ( - ctx_switches is not None - and soft_interrupts is not None - and interrupts is not None - ): - break - syscalls = 0 - return _common.scpustats( - ctx_switches, interrupts, soft_interrupts, syscalls - ) - - -def _cpu_get_cpuinfo_freq(): - """Return current CPU frequency from cpuinfo if available.""" - with open_binary(f"{get_procfs_path()}/cpuinfo") as f: - return [ - float(line.split(b':', 1)[1]) - for line in f - if line.lower().startswith(b'cpu mhz') - ] - - -if os.path.exists("/sys/devices/system/cpu/cpufreq/policy0") or os.path.exists( - "/sys/devices/system/cpu/cpu0/cpufreq" -): - - def cpu_freq(): - """Return frequency metrics for all CPUs. - Contrarily to other OSes, Linux updates these values in - real-time. - """ - cpuinfo_freqs = _cpu_get_cpuinfo_freq() - paths = glob.glob( - "/sys/devices/system/cpu/cpufreq/policy[0-9]*" - ) or glob.glob("/sys/devices/system/cpu/cpu[0-9]*/cpufreq") - paths.sort(key=lambda x: int(re.search(r"[0-9]+", x).group())) - ret = [] - pjoin = os.path.join - for i, path in enumerate(paths): - if len(paths) == len(cpuinfo_freqs): - # take cached value from cpuinfo if available, see: - # https://github.com/giampaolo/psutil/issues/1851 - curr = cpuinfo_freqs[i] * 1000 - else: - curr = bcat(pjoin(path, "scaling_cur_freq"), fallback=None) - if curr is None: - # Likely an old RedHat, see: - # https://github.com/giampaolo/psutil/issues/1071 - curr = bcat(pjoin(path, "cpuinfo_cur_freq"), fallback=None) - if curr is None: - online_path = f"/sys/devices/system/cpu/cpu{i}/online" - # if cpu core is offline, set to all zeroes - if cat(online_path, fallback=None) == "0\n": - ret.append(_common.scpufreq(0.0, 0.0, 0.0)) - continue - msg = "can't find current frequency file" - raise NotImplementedError(msg) - curr = int(curr) / 1000 - max_ = int(bcat(pjoin(path, "scaling_max_freq"))) / 1000 - min_ = int(bcat(pjoin(path, "scaling_min_freq"))) / 1000 - ret.append(_common.scpufreq(curr, min_, max_)) - return ret - -else: - - def cpu_freq(): - """Alternate implementation using /proc/cpuinfo. - min and max frequencies are not available and are set to None. - """ - return [_common.scpufreq(x, 0.0, 0.0) for x in _cpu_get_cpuinfo_freq()] - - -# ===================================================================== -# --- network -# ===================================================================== - - -net_if_addrs = cext_posix.net_if_addrs - - -class _Ipv6UnsupportedError(Exception): - pass - - -class NetConnections: - """A wrapper on top of /proc/net/* files, retrieving per-process - and system-wide open connections (TCP, UDP, UNIX) similarly to - "netstat -an". - - Note: in case of UNIX sockets we're only able to determine the - local endpoint/path, not the one it's connected to. - According to [1] it would be possible but not easily. - - [1] http://serverfault.com/a/417946 - """ - - def __init__(self): - # The string represents the basename of the corresponding - # /proc/net/{proto_name} file. - tcp4 = ("tcp", socket.AF_INET, socket.SOCK_STREAM) - tcp6 = ("tcp6", socket.AF_INET6, socket.SOCK_STREAM) - udp4 = ("udp", socket.AF_INET, socket.SOCK_DGRAM) - udp6 = ("udp6", socket.AF_INET6, socket.SOCK_DGRAM) - unix = ("unix", socket.AF_UNIX, None) - self.tmap = { - "all": (tcp4, tcp6, udp4, udp6, unix), - "tcp": (tcp4, tcp6), - "tcp4": (tcp4,), - "tcp6": (tcp6,), - "udp": (udp4, udp6), - "udp4": (udp4,), - "udp6": (udp6,), - "unix": (unix,), - "inet": (tcp4, tcp6, udp4, udp6), - "inet4": (tcp4, udp4), - "inet6": (tcp6, udp6), - } - self._procfs_path = None - - def get_proc_inodes(self, pid): - inodes = defaultdict(list) - for fd in os.listdir(f"{self._procfs_path}/{pid}/fd"): - try: - inode = readlink(f"{self._procfs_path}/{pid}/fd/{fd}") - except (FileNotFoundError, ProcessLookupError): - # ENOENT == file which is gone in the meantime; - # os.stat(f"/proc/{self.pid}") will be done later - # to force NSP (if it's the case) - continue - except OSError as err: - if err.errno == errno.EINVAL: - # not a link - continue - if err.errno == errno.ENAMETOOLONG: - # file name too long - debug(err) - continue - raise - else: - if inode.startswith('socket:['): - # the process is using a socket - inode = inode[8:][:-1] - inodes[inode].append((pid, int(fd))) - return inodes - - def get_all_inodes(self): - inodes = {} - for pid in pids(): - try: - inodes.update(self.get_proc_inodes(pid)) - except (FileNotFoundError, ProcessLookupError, PermissionError): - # os.listdir() is gonna raise a lot of access denied - # exceptions in case of unprivileged user; that's fine - # as we'll just end up returning a connection with PID - # and fd set to None anyway. - # Both netstat -an and lsof does the same so it's - # unlikely we can do any better. - # ENOENT just means a PID disappeared on us. - continue - return inodes - - @staticmethod - def decode_address(addr, family): - """Accept an "ip:port" address as displayed in /proc/net/* - and convert it into a human readable form, like: - - "0500000A:0016" -> ("10.0.0.5", 22) - "0000000000000000FFFF00000100007F:9E49" -> ("::ffff:127.0.0.1", 40521) - - The IP address portion is a little or big endian four-byte - hexadecimal number; that is, the least significant byte is listed - first, so we need to reverse the order of the bytes to convert it - to an IP address. - The port is represented as a two-byte hexadecimal number. - - Reference: - http://linuxdevcenter.com/pub/a/linux/2000/11/16/LinuxAdmin.html - """ - ip, port = addr.split(':') - port = int(port, 16) - # this usually refers to a local socket in listen mode with - # no end-points connected - if not port: - return () - ip = ip.encode('ascii') - if family == socket.AF_INET: - # see: https://github.com/giampaolo/psutil/issues/201 - if LITTLE_ENDIAN: - ip = socket.inet_ntop(family, base64.b16decode(ip)[::-1]) - else: - ip = socket.inet_ntop(family, base64.b16decode(ip)) - else: # IPv6 - ip = base64.b16decode(ip) - try: - # see: https://github.com/giampaolo/psutil/issues/201 - if LITTLE_ENDIAN: - ip = socket.inet_ntop( - socket.AF_INET6, - struct.pack('>4I', *struct.unpack('<4I', ip)), - ) - else: - ip = socket.inet_ntop( - socket.AF_INET6, - struct.pack('<4I', *struct.unpack('<4I', ip)), - ) - except ValueError: - # see: https://github.com/giampaolo/psutil/issues/623 - if not supports_ipv6(): - raise _Ipv6UnsupportedError from None - raise - return _common.addr(ip, port) - - @staticmethod - def process_inet(file, family, type_, inodes, filter_pid=None): - """Parse /proc/net/tcp* and /proc/net/udp* files.""" - if file.endswith('6') and not os.path.exists(file): - # IPv6 not supported - return - with open_text(file) as f: - f.readline() # skip the first line - for lineno, line in enumerate(f, 1): - try: - _, laddr, raddr, status, _, _, _, _, _, inode = ( - line.split()[:10] - ) - except ValueError: - msg = ( - f"error while parsing {file}; malformed line" - f" {lineno} {line!r}" - ) - raise RuntimeError(msg) from None - if inode in inodes: - # # We assume inet sockets are unique, so we error - # # out if there are multiple references to the - # # same inode. We won't do this for UNIX sockets. - # if len(inodes[inode]) > 1 and family != socket.AF_UNIX: - # raise ValueError("ambiguous inode with multiple " - # "PIDs references") - pid, fd = inodes[inode][0] - else: - pid, fd = None, -1 - if filter_pid is not None and filter_pid != pid: - continue - else: - if type_ == socket.SOCK_STREAM: - status = TCP_STATUSES[status] - else: - status = _common.CONN_NONE - try: - laddr = NetConnections.decode_address(laddr, family) - raddr = NetConnections.decode_address(raddr, family) - except _Ipv6UnsupportedError: - continue - yield (fd, family, type_, laddr, raddr, status, pid) - - @staticmethod - def process_unix(file, family, inodes, filter_pid=None): - """Parse /proc/net/unix files.""" - with open_text(file) as f: - f.readline() # skip the first line - for line in f: - tokens = line.split() - try: - _, _, _, _, type_, _, inode = tokens[0:7] - except ValueError: - if ' ' not in line: - # see: https://github.com/giampaolo/psutil/issues/766 - continue - msg = ( - f"error while parsing {file}; malformed line {line!r}" - ) - raise RuntimeError(msg) # noqa: B904 - if inode in inodes: # noqa: SIM108 - # With UNIX sockets we can have a single inode - # referencing many file descriptors. - pairs = inodes[inode] - else: - pairs = [(None, -1)] - for pid, fd in pairs: - if filter_pid is not None and filter_pid != pid: - continue - else: - path = tokens[-1] if len(tokens) == 8 else '' - type_ = _common.socktype_to_enum(int(type_)) - # XXX: determining the remote endpoint of a - # UNIX socket on Linux is not possible, see: - # https://serverfault.com/questions/252723/ - raddr = "" - status = _common.CONN_NONE - yield (fd, family, type_, path, raddr, status, pid) - - def retrieve(self, kind, pid=None): - self._procfs_path = get_procfs_path() - if pid is not None: - inodes = self.get_proc_inodes(pid) - if not inodes: - # no connections for this process - return [] - else: - inodes = self.get_all_inodes() - ret = set() - for proto_name, family, type_ in self.tmap[kind]: - path = f"{self._procfs_path}/net/{proto_name}" - if family in {socket.AF_INET, socket.AF_INET6}: - ls = self.process_inet( - path, family, type_, inodes, filter_pid=pid - ) - else: - ls = self.process_unix(path, family, inodes, filter_pid=pid) - for fd, family, type_, laddr, raddr, status, bound_pid in ls: - if pid: - conn = _common.pconn( - fd, family, type_, laddr, raddr, status - ) - else: - conn = _common.sconn( - fd, family, type_, laddr, raddr, status, bound_pid - ) - ret.add(conn) - return list(ret) - - -_net_connections = NetConnections() - - -def net_connections(kind='inet'): - """Return system-wide open connections.""" - return _net_connections.retrieve(kind) - - -def net_io_counters(): - """Return network I/O statistics for every network interface - installed on the system as a dict of raw tuples. - """ - with open_text(f"{get_procfs_path()}/net/dev") as f: - lines = f.readlines() - retdict = {} - for line in lines[2:]: - colon = line.rfind(':') - assert colon > 0, repr(line) - name = line[:colon].strip() - fields = line[colon + 1 :].strip().split() - - ( - # in - bytes_recv, - packets_recv, - errin, - dropin, - _fifoin, # unused - _framein, # unused - _compressedin, # unused - _multicastin, # unused - # out - bytes_sent, - packets_sent, - errout, - dropout, - _fifoout, # unused - _collisionsout, # unused - _carrierout, # unused - _compressedout, # unused - ) = map(int, fields) - - retdict[name] = ( - bytes_sent, - bytes_recv, - packets_sent, - packets_recv, - errin, - errout, - dropin, - dropout, - ) - return retdict - - -def net_if_stats(): - """Get NIC stats (isup, duplex, speed, mtu).""" - duplex_map = { - cext.DUPLEX_FULL: NIC_DUPLEX_FULL, - cext.DUPLEX_HALF: NIC_DUPLEX_HALF, - cext.DUPLEX_UNKNOWN: NIC_DUPLEX_UNKNOWN, - } - names = net_io_counters().keys() - ret = {} - for name in names: - try: - mtu = cext_posix.net_if_mtu(name) - flags = cext_posix.net_if_flags(name) - duplex, speed = cext.net_if_duplex_speed(name) - except OSError as err: - # https://github.com/giampaolo/psutil/issues/1279 - if err.errno != errno.ENODEV: - raise - debug(err) - else: - output_flags = ','.join(flags) - isup = 'running' in flags - ret[name] = _common.snicstats( - isup, duplex_map[duplex], speed, mtu, output_flags - ) - return ret - - -# ===================================================================== -# --- disks -# ===================================================================== - - -disk_usage = _psposix.disk_usage - - -def disk_io_counters(perdisk=False): - """Return disk I/O statistics for every disk installed on the - system as a dict of raw tuples. - """ - - def read_procfs(): - # OK, this is a bit confusing. The format of /proc/diskstats can - # have 3 variations. - # On Linux 2.4 each line has always 15 fields, e.g.: - # "3 0 8 hda 8 8 8 8 8 8 8 8 8 8 8" - # On Linux 2.6+ each line *usually* has 14 fields, and the disk - # name is in another position, like this: - # "3 0 hda 8 8 8 8 8 8 8 8 8 8 8" - # ...unless (Linux 2.6) the line refers to a partition instead - # of a disk, in which case the line has less fields (7): - # "3 1 hda1 8 8 8 8" - # 4.18+ has 4 fields added: - # "3 0 hda 8 8 8 8 8 8 8 8 8 8 8 0 0 0 0" - # 5.5 has 2 more fields. - # See: - # https://www.kernel.org/doc/Documentation/iostats.txt - # https://www.kernel.org/doc/Documentation/ABI/testing/procfs-diskstats - with open_text(f"{get_procfs_path()}/diskstats") as f: - lines = f.readlines() - for line in lines: - fields = line.split() - flen = len(fields) - # fmt: off - if flen == 15: - # Linux 2.4 - name = fields[3] - reads = int(fields[2]) - (reads_merged, rbytes, rtime, writes, writes_merged, - wbytes, wtime, _, busy_time, _) = map(int, fields[4:14]) - elif flen == 14 or flen >= 18: - # Linux 2.6+, line referring to a disk - name = fields[2] - (reads, reads_merged, rbytes, rtime, writes, writes_merged, - wbytes, wtime, _, busy_time, _) = map(int, fields[3:14]) - elif flen == 7: - # Linux 2.6+, line referring to a partition - name = fields[2] - reads, rbytes, writes, wbytes = map(int, fields[3:]) - rtime = wtime = reads_merged = writes_merged = busy_time = 0 - else: - msg = f"not sure how to interpret line {line!r}" - raise ValueError(msg) - yield (name, reads, writes, rbytes, wbytes, rtime, wtime, - reads_merged, writes_merged, busy_time) - # fmt: on - - def read_sysfs(): - for block in os.listdir('/sys/block'): - for root, _, files in os.walk(os.path.join('/sys/block', block)): - if 'stat' not in files: - continue - with open_text(os.path.join(root, 'stat')) as f: - fields = f.read().strip().split() - name = os.path.basename(root) - # fmt: off - (reads, reads_merged, rbytes, rtime, writes, writes_merged, - wbytes, wtime, _, busy_time) = map(int, fields[:10]) - yield (name, reads, writes, rbytes, wbytes, rtime, - wtime, reads_merged, writes_merged, busy_time) - # fmt: on - - if os.path.exists(f"{get_procfs_path()}/diskstats"): - gen = read_procfs() - elif os.path.exists('/sys/block'): - gen = read_sysfs() - else: - msg = ( - f"{get_procfs_path()}/diskstats nor /sys/block are available on" - " this system" - ) - raise NotImplementedError(msg) - - retdict = {} - for entry in gen: - # fmt: off - (name, reads, writes, rbytes, wbytes, rtime, wtime, reads_merged, - writes_merged, busy_time) = entry - if not perdisk and not is_storage_device(name): - # perdisk=False means we want to calculate totals so we skip - # partitions (e.g. 'sda1', 'nvme0n1p1') and only include - # base disk devices (e.g. 'sda', 'nvme0n1'). Base disks - # include a total of all their partitions + some extra size - # of their own: - # $ cat /proc/diskstats - # 259 0 sda 10485760 ... - # 259 1 sda1 5186039 ... - # 259 1 sda2 5082039 ... - # See: - # https://github.com/giampaolo/psutil/pull/1313 - continue - - rbytes *= DISK_SECTOR_SIZE - wbytes *= DISK_SECTOR_SIZE - retdict[name] = (reads, writes, rbytes, wbytes, rtime, wtime, - reads_merged, writes_merged, busy_time) - # fmt: on - - return retdict - - -class RootFsDeviceFinder: - """disk_partitions() may return partitions with device == "/dev/root" - or "rootfs". This container class uses different strategies to try to - obtain the real device path. Resources: - https://bootlin.com/blog/find-root-device/ - https://www.systutorials.com/how-to-find-the-disk-where-root-is-on-in-bash-on-linux/. - """ - - __slots__ = ['major', 'minor'] - - def __init__(self): - dev = os.stat("/").st_dev - self.major = os.major(dev) - self.minor = os.minor(dev) - - def ask_proc_partitions(self): - with open_text(f"{get_procfs_path()}/partitions") as f: - for line in f.readlines()[2:]: - fields = line.split() - if len(fields) < 4: # just for extra safety - continue - major = int(fields[0]) if fields[0].isdigit() else None - minor = int(fields[1]) if fields[1].isdigit() else None - name = fields[3] - if major == self.major and minor == self.minor: - if name: # just for extra safety - return f"/dev/{name}" - - def ask_sys_dev_block(self): - path = f"/sys/dev/block/{self.major}:{self.minor}/uevent" - with open_text(path) as f: - for line in f: - if line.startswith("DEVNAME="): - name = line.strip().rpartition("DEVNAME=")[2] - if name: # just for extra safety - return f"/dev/{name}" - - def ask_sys_class_block(self): - needle = f"{self.major}:{self.minor}" - files = glob.iglob("/sys/class/block/*/dev") - for file in files: - try: - f = open_text(file) - except FileNotFoundError: # race condition - continue - else: - with f: - data = f.read().strip() - if data == needle: - name = os.path.basename(os.path.dirname(file)) - return f"/dev/{name}" - - def find(self): - path = None - if path is None: - try: - path = self.ask_proc_partitions() - except OSError as err: - debug(err) - if path is None: - try: - path = self.ask_sys_dev_block() - except OSError as err: - debug(err) - if path is None: - try: - path = self.ask_sys_class_block() - except OSError as err: - debug(err) - # We use exists() because the "/dev/*" part of the path is hard - # coded, so we want to be sure. - if path is not None and os.path.exists(path): - return path - - -def disk_partitions(all=False): - """Return mounted disk partitions as a list of namedtuples.""" - fstypes = set() - procfs_path = get_procfs_path() - if not all: - with open_text(f"{procfs_path}/filesystems") as f: - for line in f: - line = line.strip() - if not line.startswith("nodev"): - fstypes.add(line.strip()) - else: - # ignore all lines starting with "nodev" except "nodev zfs" - fstype = line.split("\t")[1] - if fstype == "zfs": - fstypes.add("zfs") - - # See: https://github.com/giampaolo/psutil/issues/1307 - if procfs_path == "/proc" and os.path.isfile('/etc/mtab'): - mounts_path = os.path.realpath("/etc/mtab") - else: - mounts_path = os.path.realpath(f"{procfs_path}/self/mounts") - - retlist = [] - partitions = cext.disk_partitions(mounts_path) - for partition in partitions: - device, mountpoint, fstype, opts = partition - if device == 'none': - device = '' - if device in {"/dev/root", "rootfs"}: - device = RootFsDeviceFinder().find() or device - if not all: - if not device or fstype not in fstypes: - continue - ntuple = _common.sdiskpart(device, mountpoint, fstype, opts) - retlist.append(ntuple) - - return retlist - - -# ===================================================================== -# --- sensors -# ===================================================================== - - -def sensors_temperatures(): - """Return hardware (CPU and others) temperatures as a dict - including hardware name, label, current, max and critical - temperatures. - - Implementation notes: - - /sys/class/hwmon looks like the most recent interface to - retrieve this info, and this implementation relies on it - only (old distros will probably use something else) - - lm-sensors on Ubuntu 16.04 relies on /sys/class/hwmon - - /sys/class/thermal/thermal_zone* is another one but it's more - difficult to parse - """ - ret = collections.defaultdict(list) - basenames = glob.glob('/sys/class/hwmon/hwmon*/temp*_*') - # CentOS has an intermediate /device directory: - # https://github.com/giampaolo/psutil/issues/971 - # https://github.com/nicolargo/glances/issues/1060 - basenames.extend(glob.glob('/sys/class/hwmon/hwmon*/device/temp*_*')) - basenames = sorted({x.split('_')[0] for x in basenames}) - - # Only add the coretemp hwmon entries if they're not already in - # /sys/class/hwmon/ - # https://github.com/giampaolo/psutil/issues/1708 - # https://github.com/giampaolo/psutil/pull/1648 - basenames2 = glob.glob( - '/sys/devices/platform/coretemp.*/hwmon/hwmon*/temp*_*' - ) - repl = re.compile(r"/sys/devices/platform/coretemp.*/hwmon/") - for name in basenames2: - altname = repl.sub('/sys/class/hwmon/', name) - if altname not in basenames: - basenames.append(name) - - for base in basenames: - try: - path = base + '_input' - current = float(bcat(path)) / 1000.0 - path = os.path.join(os.path.dirname(base), 'name') - unit_name = cat(path).strip() - except (OSError, ValueError): - # A lot of things can go wrong here, so let's just skip the - # whole entry. Sure thing is Linux's /sys/class/hwmon really - # is a stinky broken mess. - # https://github.com/giampaolo/psutil/issues/1009 - # https://github.com/giampaolo/psutil/issues/1101 - # https://github.com/giampaolo/psutil/issues/1129 - # https://github.com/giampaolo/psutil/issues/1245 - # https://github.com/giampaolo/psutil/issues/1323 - continue - - high = bcat(base + '_max', fallback=None) - critical = bcat(base + '_crit', fallback=None) - label = cat(base + '_label', fallback='').strip() - - if high is not None: - try: - high = float(high) / 1000.0 - except ValueError: - high = None - if critical is not None: - try: - critical = float(critical) / 1000.0 - except ValueError: - critical = None - - ret[unit_name].append((label, current, high, critical)) - - # Indication that no sensors were detected in /sys/class/hwmon/ - if not basenames: - basenames = glob.glob('/sys/class/thermal/thermal_zone*') - basenames = sorted(set(basenames)) - - for base in basenames: - try: - path = os.path.join(base, 'temp') - current = float(bcat(path)) / 1000.0 - path = os.path.join(base, 'type') - unit_name = cat(path).strip() - except (OSError, ValueError) as err: - debug(err) - continue - - trip_paths = glob.glob(base + '/trip_point*') - trip_points = { - '_'.join(os.path.basename(p).split('_')[0:3]) - for p in trip_paths - } - critical = None - high = None - for trip_point in trip_points: - path = os.path.join(base, trip_point + "_type") - trip_type = cat(path, fallback='').strip() - if trip_type == 'critical': - critical = bcat( - os.path.join(base, trip_point + "_temp"), fallback=None - ) - elif trip_type == 'high': - high = bcat( - os.path.join(base, trip_point + "_temp"), fallback=None - ) - - if high is not None: - try: - high = float(high) / 1000.0 - except ValueError: - high = None - if critical is not None: - try: - critical = float(critical) / 1000.0 - except ValueError: - critical = None - - ret[unit_name].append(('', current, high, critical)) - - return dict(ret) - - -def sensors_fans(): - """Return hardware fans info (for CPU and other peripherals) as a - dict including hardware label and current speed. - - Implementation notes: - - /sys/class/hwmon looks like the most recent interface to - retrieve this info, and this implementation relies on it - only (old distros will probably use something else) - - lm-sensors on Ubuntu 16.04 relies on /sys/class/hwmon - """ - ret = collections.defaultdict(list) - basenames = glob.glob('/sys/class/hwmon/hwmon*/fan*_*') - if not basenames: - # CentOS has an intermediate /device directory: - # https://github.com/giampaolo/psutil/issues/971 - basenames = glob.glob('/sys/class/hwmon/hwmon*/device/fan*_*') - - basenames = sorted({x.split("_")[0] for x in basenames}) - for base in basenames: - try: - current = int(bcat(base + '_input')) - except OSError as err: - debug(err) - continue - unit_name = cat(os.path.join(os.path.dirname(base), 'name')).strip() - label = cat(base + '_label', fallback='').strip() - ret[unit_name].append(_common.sfan(label, current)) - - return dict(ret) - - -def sensors_battery(): - """Return battery information. - Implementation note: it appears /sys/class/power_supply/BAT0/ - directory structure may vary and provide files with the same - meaning but under different names, see: - https://github.com/giampaolo/psutil/issues/966. - """ - null = object() - - def multi_bcat(*paths): - """Attempt to read the content of multiple files which may - not exist. If none of them exist return None. - """ - for path in paths: - ret = bcat(path, fallback=null) - if ret != null: - try: - return int(ret) - except ValueError: - return ret.strip() - return None - - bats = [ - x - for x in os.listdir(POWER_SUPPLY_PATH) - if x.startswith('BAT') or 'battery' in x.lower() - ] - if not bats: - return None - # Get the first available battery. Usually this is "BAT0", except - # some rare exceptions: - # https://github.com/giampaolo/psutil/issues/1238 - root = os.path.join(POWER_SUPPLY_PATH, min(bats)) - - # Base metrics. - energy_now = multi_bcat(root + "/energy_now", root + "/charge_now") - power_now = multi_bcat(root + "/power_now", root + "/current_now") - energy_full = multi_bcat(root + "/energy_full", root + "/charge_full") - time_to_empty = multi_bcat(root + "/time_to_empty_now") - - # Percent. If we have energy_full the percentage will be more - # accurate compared to reading /capacity file (float vs. int). - if energy_full is not None and energy_now is not None: - try: - percent = 100.0 * energy_now / energy_full - except ZeroDivisionError: - percent = 0.0 - else: - percent = int(cat(root + "/capacity", fallback=-1)) - if percent == -1: - return None - - # Is AC power cable plugged in? - # Note: AC0 is not always available and sometimes (e.g. CentOS7) - # it's called "AC". - power_plugged = None - online = multi_bcat( - os.path.join(POWER_SUPPLY_PATH, "AC0/online"), - os.path.join(POWER_SUPPLY_PATH, "AC/online"), - ) - if online is not None: - power_plugged = online == 1 - else: - status = cat(root + "/status", fallback="").strip().lower() - if status == "discharging": - power_plugged = False - elif status in {"charging", "full"}: - power_plugged = True - - # Seconds left. - # Note to self: we may also calculate the charging ETA as per: - # https://github.com/thialfihar/dotfiles/blob/ - # 013937745fd9050c30146290e8f963d65c0179e6/bin/battery.py#L55 - if power_plugged: - secsleft = _common.POWER_TIME_UNLIMITED - elif energy_now is not None and power_now is not None: - try: - secsleft = int(energy_now / power_now * 3600) - except ZeroDivisionError: - secsleft = _common.POWER_TIME_UNKNOWN - elif time_to_empty is not None: - secsleft = int(time_to_empty * 60) - if secsleft < 0: - secsleft = _common.POWER_TIME_UNKNOWN - else: - secsleft = _common.POWER_TIME_UNKNOWN - - return _common.sbattery(percent, secsleft, power_plugged) - - -# ===================================================================== -# --- other system functions -# ===================================================================== - - -def users(): - """Return currently connected users as a list of namedtuples.""" - retlist = [] - rawlist = cext.users() - for item in rawlist: - user, tty, hostname, tstamp, pid = item - nt = _common.suser(user, tty or None, hostname, tstamp, pid) - retlist.append(nt) - return retlist - - -def boot_time(): - """Return the system boot time expressed in seconds since the epoch.""" - global BOOT_TIME - path = f"{get_procfs_path()}/stat" - with open_binary(path) as f: - for line in f: - if line.startswith(b'btime'): - ret = float(line.strip().split()[1]) - BOOT_TIME = ret - return ret - msg = f"line 'btime' not found in {path}" - raise RuntimeError(msg) - - -# ===================================================================== -# --- processes -# ===================================================================== - - -def pids(): - """Returns a list of PIDs currently running on the system.""" - path = get_procfs_path().encode(ENCODING) - return [int(x) for x in os.listdir(path) if x.isdigit()] - - -def pid_exists(pid): - """Check for the existence of a unix PID. Linux TIDs are not - supported (always return False). - """ - if not _psposix.pid_exists(pid): - return False - else: - # Linux's apparently does not distinguish between PIDs and TIDs - # (thread IDs). - # listdir("/proc") won't show any TID (only PIDs) but - # os.stat("/proc/{tid}") will succeed if {tid} exists. - # os.kill() can also be passed a TID. This is quite confusing. - # In here we want to enforce this distinction and support PIDs - # only, see: - # https://github.com/giampaolo/psutil/issues/687 - try: - # Note: already checked that this is faster than using a - # regular expr. Also (a lot) faster than doing - # 'return pid in pids()' - path = f"{get_procfs_path()}/{pid}/status" - with open_binary(path) as f: - for line in f: - if line.startswith(b"Tgid:"): - tgid = int(line.split()[1]) - # If tgid and pid are the same then we're - # dealing with a process PID. - return tgid == pid - msg = f"'Tgid' line not found in {path}" - raise ValueError(msg) - except (OSError, ValueError): - return pid in pids() - - -def ppid_map(): - """Obtain a {pid: ppid, ...} dict for all running processes in - one shot. Used to speed up Process.children(). - """ - ret = {} - procfs_path = get_procfs_path() - for pid in pids(): - try: - with open_binary(f"{procfs_path}/{pid}/stat") as f: - data = f.read() - except (FileNotFoundError, ProcessLookupError): - # Note: we should be able to access /stat for all processes - # aka it's unlikely we'll bump into EPERM, which is good. - pass - else: - rpar = data.rfind(b')') - dset = data[rpar + 2 :].split() - ppid = int(dset[1]) - ret[pid] = ppid - return ret - - -def wrap_exceptions(fun): - """Decorator which translates bare OSError and OSError exceptions - into NoSuchProcess and AccessDenied. - """ - - @functools.wraps(fun) - def wrapper(self, *args, **kwargs): - pid, name = self.pid, self._name - try: - return fun(self, *args, **kwargs) - except PermissionError as err: - raise AccessDenied(pid, name) from err - except ProcessLookupError as err: - self._raise_if_zombie() - raise NoSuchProcess(pid, name) from err - except FileNotFoundError as err: - self._raise_if_zombie() - # /proc/PID directory may still exist, but the files within - # it may not, indicating the process is gone, see: - # https://github.com/giampaolo/psutil/issues/2418 - if not os.path.exists(f"{self._procfs_path}/{pid}/stat"): - raise NoSuchProcess(pid, name) from err - raise - - return wrapper - - -class Process: - """Linux process implementation.""" - - __slots__ = ["_cache", "_name", "_ppid", "_procfs_path", "pid"] - - def __init__(self, pid): - self.pid = pid - self._name = None - self._ppid = None - self._procfs_path = get_procfs_path() - - def _is_zombie(self): - # Note: most of the times Linux is able to return info about the - # process even if it's a zombie, and /proc/{pid} will exist. - # There are some exceptions though, like exe(), cmdline() and - # memory_maps(). In these cases /proc/{pid}/{file} exists but - # it's empty. Instead of returning a "null" value we'll raise an - # exception. - try: - data = bcat(f"{self._procfs_path}/{self.pid}/stat") - except OSError: - return False - else: - rpar = data.rfind(b')') - status = data[rpar + 2 : rpar + 3] - return status == b"Z" - - def _raise_if_zombie(self): - if self._is_zombie(): - raise ZombieProcess(self.pid, self._name, self._ppid) - - def _raise_if_not_alive(self): - """Raise NSP if the process disappeared on us.""" - # For those C function who do not raise NSP, possibly returning - # incorrect or incomplete result. - os.stat(f"{self._procfs_path}/{self.pid}") - - @wrap_exceptions - @memoize_when_activated - def _parse_stat_file(self): - """Parse /proc/{pid}/stat file and return a dict with various - process info. - Using "man proc" as a reference: where "man proc" refers to - position N always subtract 3 (e.g ppid position 4 in - 'man proc' == position 1 in here). - The return value is cached in case oneshot() ctx manager is - in use. - """ - data = bcat(f"{self._procfs_path}/{self.pid}/stat") - # Process name is between parentheses. It can contain spaces and - # other parentheses. This is taken into account by looking for - # the first occurrence of "(" and the last occurrence of ")". - rpar = data.rfind(b')') - name = data[data.find(b'(') + 1 : rpar] - fields = data[rpar + 2 :].split() - - ret = {} - ret['name'] = name - ret['status'] = fields[0] - ret['ppid'] = fields[1] - ret['ttynr'] = fields[4] - ret['utime'] = fields[11] - ret['stime'] = fields[12] - ret['children_utime'] = fields[13] - ret['children_stime'] = fields[14] - ret['create_time'] = fields[19] - ret['cpu_num'] = fields[36] - try: - ret['blkio_ticks'] = fields[39] # aka 'delayacct_blkio_ticks' - except IndexError: - # https://github.com/giampaolo/psutil/issues/2455 - debug("can't get blkio_ticks, set iowait to 0") - ret['blkio_ticks'] = 0 - - return ret - - @wrap_exceptions - @memoize_when_activated - def _read_status_file(self): - """Read /proc/{pid}/stat file and return its content. - The return value is cached in case oneshot() ctx manager is - in use. - """ - with open_binary(f"{self._procfs_path}/{self.pid}/status") as f: - return f.read() - - @wrap_exceptions - @memoize_when_activated - def _read_smaps_file(self): - with open_binary(f"{self._procfs_path}/{self.pid}/smaps") as f: - return f.read().strip() - - def oneshot_enter(self): - self._parse_stat_file.cache_activate(self) - self._read_status_file.cache_activate(self) - self._read_smaps_file.cache_activate(self) - - def oneshot_exit(self): - self._parse_stat_file.cache_deactivate(self) - self._read_status_file.cache_deactivate(self) - self._read_smaps_file.cache_deactivate(self) - - @wrap_exceptions - def name(self): - # XXX - gets changed later and probably needs refactoring - return decode(self._parse_stat_file()['name']) - - @wrap_exceptions - def exe(self): - try: - return readlink(f"{self._procfs_path}/{self.pid}/exe") - except (FileNotFoundError, ProcessLookupError): - self._raise_if_zombie() - # no such file error; might be raised also if the - # path actually exists for system processes with - # low pids (about 0-20) - if os.path.lexists(f"{self._procfs_path}/{self.pid}"): - return "" - raise - - @wrap_exceptions - def cmdline(self): - with open_text(f"{self._procfs_path}/{self.pid}/cmdline") as f: - data = f.read() - if not data: - # may happen in case of zombie process - self._raise_if_zombie() - return [] - # 'man proc' states that args are separated by null bytes '\0' - # and last char is supposed to be a null byte. Nevertheless - # some processes may change their cmdline after being started - # (via setproctitle() or similar), they are usually not - # compliant with this rule and use spaces instead. Google - # Chrome process is an example. See: - # https://github.com/giampaolo/psutil/issues/1179 - sep = '\x00' if data.endswith('\x00') else ' ' - if data.endswith(sep): - data = data[:-1] - cmdline = data.split(sep) - # Sometimes last char is a null byte '\0' but the args are - # separated by spaces, see: https://github.com/giampaolo/psutil/ - # issues/1179#issuecomment-552984549 - if sep == '\x00' and len(cmdline) == 1 and ' ' in data: - cmdline = data.split(' ') - return cmdline - - @wrap_exceptions - def environ(self): - with open_text(f"{self._procfs_path}/{self.pid}/environ") as f: - data = f.read() - return parse_environ_block(data) - - @wrap_exceptions - def terminal(self): - tty_nr = int(self._parse_stat_file()['ttynr']) - tmap = _psposix.get_terminal_map() - try: - return tmap[tty_nr] - except KeyError: - return None - - # May not be available on old kernels. - if os.path.exists(f"/proc/{os.getpid()}/io"): - - @wrap_exceptions - def io_counters(self): - fname = f"{self._procfs_path}/{self.pid}/io" - fields = {} - with open_binary(fname) as f: - for line in f: - # https://github.com/giampaolo/psutil/issues/1004 - line = line.strip() - if line: - try: - name, value = line.split(b': ') - except ValueError: - # https://github.com/giampaolo/psutil/issues/1004 - continue - else: - fields[name] = int(value) - if not fields: - msg = f"{fname} file was empty" - raise RuntimeError(msg) - try: - return pio( - fields[b'syscr'], # read syscalls - fields[b'syscw'], # write syscalls - fields[b'read_bytes'], # read bytes - fields[b'write_bytes'], # write bytes - fields[b'rchar'], # read chars - fields[b'wchar'], # write chars - ) - except KeyError as err: - msg = ( - f"{err.args[0]!r} field was not found in {fname}; found" - f" fields are {fields!r}" - ) - raise ValueError(msg) from None - - @wrap_exceptions - def cpu_times(self): - values = self._parse_stat_file() - utime = float(values['utime']) / CLOCK_TICKS - stime = float(values['stime']) / CLOCK_TICKS - children_utime = float(values['children_utime']) / CLOCK_TICKS - children_stime = float(values['children_stime']) / CLOCK_TICKS - iowait = float(values['blkio_ticks']) / CLOCK_TICKS - return pcputimes(utime, stime, children_utime, children_stime, iowait) - - @wrap_exceptions - def cpu_num(self): - """What CPU the process is on.""" - return int(self._parse_stat_file()['cpu_num']) - - @wrap_exceptions - def wait(self, timeout=None): - return _psposix.wait_pid(self.pid, timeout, self._name) - - @wrap_exceptions - def create_time(self): - ctime = float(self._parse_stat_file()['create_time']) - # According to documentation, starttime is in field 21 and the - # unit is jiffies (clock ticks). - # We first divide it for clock ticks and then add uptime returning - # seconds since the epoch. - # Also use cached value if available. - bt = BOOT_TIME or boot_time() - return (ctime / CLOCK_TICKS) + bt - - @wrap_exceptions - def memory_info(self): - # ============================================================ - # | FIELD | DESCRIPTION | AKA | TOP | - # ============================================================ - # | rss | resident set size | | RES | - # | vms | total program size | size | VIRT | - # | shared | shared pages (from shared mappings) | | SHR | - # | text | text ('code') | trs | CODE | - # | lib | library (unused in Linux 2.6) | lrs | | - # | data | data + stack | drs | DATA | - # | dirty | dirty pages (unused in Linux 2.6) | dt | | - # ============================================================ - with open_binary(f"{self._procfs_path}/{self.pid}/statm") as f: - vms, rss, shared, text, lib, data, dirty = ( - int(x) * PAGESIZE for x in f.readline().split()[:7] - ) - return pmem(rss, vms, shared, text, lib, data, dirty) - - if HAS_PROC_SMAPS_ROLLUP or HAS_PROC_SMAPS: - - def _parse_smaps_rollup(self): - # /proc/pid/smaps_rollup was added to Linux in 2017. Faster - # than /proc/pid/smaps. It reports higher PSS than */smaps - # (from 1k up to 200k higher; tested against all processes). - # IMPORTANT: /proc/pid/smaps_rollup is weird, because it - # raises ESRCH / ENOENT for many PIDs, even if they're alive - # (also as root). In that case we'll use /proc/pid/smaps as - # fallback, which is slower but has a +50% success rate - # compared to /proc/pid/smaps_rollup. - uss = pss = swap = 0 - with open_binary( - f"{self._procfs_path}/{self.pid}/smaps_rollup" - ) as f: - for line in f: - if line.startswith(b"Private_"): - # Private_Clean, Private_Dirty, Private_Hugetlb - uss += int(line.split()[1]) * 1024 - elif line.startswith(b"Pss:"): - pss = int(line.split()[1]) * 1024 - elif line.startswith(b"Swap:"): - swap = int(line.split()[1]) * 1024 - return (uss, pss, swap) - - @wrap_exceptions - def _parse_smaps( - self, - # Gets Private_Clean, Private_Dirty, Private_Hugetlb. - _private_re=re.compile(br"\nPrivate.*:\s+(\d+)"), - _pss_re=re.compile(br"\nPss\:\s+(\d+)"), - _swap_re=re.compile(br"\nSwap\:\s+(\d+)"), - ): - # /proc/pid/smaps does not exist on kernels < 2.6.14 or if - # CONFIG_MMU kernel configuration option is not enabled. - - # Note: using 3 regexes is faster than reading the file - # line by line. - # - # You might be tempted to calculate USS by subtracting - # the "shared" value from the "resident" value in - # /proc//statm. But at least on Linux, statm's "shared" - # value actually counts pages backed by files, which has - # little to do with whether the pages are actually shared. - # /proc/self/smaps on the other hand appears to give us the - # correct information. - smaps_data = self._read_smaps_file() - # Note: smaps file can be empty for certain processes. - # The code below will not crash though and will result to 0. - uss = sum(map(int, _private_re.findall(smaps_data))) * 1024 - pss = sum(map(int, _pss_re.findall(smaps_data))) * 1024 - swap = sum(map(int, _swap_re.findall(smaps_data))) * 1024 - return (uss, pss, swap) - - @wrap_exceptions - def memory_full_info(self): - if HAS_PROC_SMAPS_ROLLUP: # faster - try: - uss, pss, swap = self._parse_smaps_rollup() - except (ProcessLookupError, FileNotFoundError): - uss, pss, swap = self._parse_smaps() - else: - uss, pss, swap = self._parse_smaps() - basic_mem = self.memory_info() - return pfullmem(*basic_mem + (uss, pss, swap)) - - else: - memory_full_info = memory_info - - if HAS_PROC_SMAPS: - - @wrap_exceptions - def memory_maps(self): - """Return process's mapped memory regions as a list of named - tuples. Fields are explained in 'man proc'; here is an updated - (Apr 2012) version: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/filesystems/proc.txt?id=b76437579d1344b612cf1851ae610c636cec7db0. - - /proc/{PID}/smaps does not exist on kernels < 2.6.14 or if - CONFIG_MMU kernel configuration option is not enabled. - """ - - def get_blocks(lines, current_block): - data = {} - for line in lines: - fields = line.split(None, 5) - if not fields[0].endswith(b':'): - # new block section - yield (current_block.pop(), data) - current_block.append(line) - else: - try: - data[fields[0]] = int(fields[1]) * 1024 - except ValueError: - if fields[0].startswith(b'VmFlags:'): - # see issue #369 - continue - msg = f"don't know how to interpret line {line!r}" - raise ValueError(msg) from None - yield (current_block.pop(), data) - - data = self._read_smaps_file() - # Note: smaps file can be empty for certain processes or for - # zombies. - if not data: - self._raise_if_zombie() - return [] - lines = data.split(b'\n') - ls = [] - first_line = lines.pop(0) - current_block = [first_line] - for header, data in get_blocks(lines, current_block): - hfields = header.split(None, 5) - try: - addr, perms, _offset, _dev, _inode, path = hfields - except ValueError: - addr, perms, _offset, _dev, _inode, path = hfields + [''] - if not path: - path = '[anon]' - else: - path = decode(path) - path = path.strip() - if path.endswith(' (deleted)') and not path_exists_strict( - path - ): - path = path[:-10] - item = ( - decode(addr), - decode(perms), - path, - data.get(b'Rss:', 0), - data.get(b'Size:', 0), - data.get(b'Pss:', 0), - data.get(b'Shared_Clean:', 0), - data.get(b'Shared_Dirty:', 0), - data.get(b'Private_Clean:', 0), - data.get(b'Private_Dirty:', 0), - data.get(b'Referenced:', 0), - data.get(b'Anonymous:', 0), - data.get(b'Swap:', 0), - ) - ls.append(item) - return ls - - @wrap_exceptions - def cwd(self): - return readlink(f"{self._procfs_path}/{self.pid}/cwd") - - @wrap_exceptions - def num_ctx_switches( - self, _ctxsw_re=re.compile(br'ctxt_switches:\t(\d+)') - ): - data = self._read_status_file() - ctxsw = _ctxsw_re.findall(data) - if not ctxsw: - msg = ( - "'voluntary_ctxt_switches' and" - " 'nonvoluntary_ctxt_switches'lines were not found in" - f" {self._procfs_path}/{self.pid}/status; the kernel is" - " probably older than 2.6.23" - ) - raise NotImplementedError(msg) - return _common.pctxsw(int(ctxsw[0]), int(ctxsw[1])) - - @wrap_exceptions - def num_threads(self, _num_threads_re=re.compile(br'Threads:\t(\d+)')): - # Using a re is faster than iterating over file line by line. - data = self._read_status_file() - return int(_num_threads_re.findall(data)[0]) - - @wrap_exceptions - def threads(self): - thread_ids = os.listdir(f"{self._procfs_path}/{self.pid}/task") - thread_ids.sort() - retlist = [] - hit_enoent = False - for thread_id in thread_ids: - fname = f"{self._procfs_path}/{self.pid}/task/{thread_id}/stat" - try: - with open_binary(fname) as f: - st = f.read().strip() - except (FileNotFoundError, ProcessLookupError): - # no such file or directory or no such process; - # it means thread disappeared on us - hit_enoent = True - continue - # ignore the first two values ("pid (exe)") - st = st[st.find(b')') + 2 :] - values = st.split(b' ') - utime = float(values[11]) / CLOCK_TICKS - stime = float(values[12]) / CLOCK_TICKS - ntuple = _common.pthread(int(thread_id), utime, stime) - retlist.append(ntuple) - if hit_enoent: - self._raise_if_not_alive() - return retlist - - @wrap_exceptions - def nice_get(self): - # with open_text(f"{self._procfs_path}/{self.pid}/stat") as f: - # data = f.read() - # return int(data.split()[18]) - - # Use C implementation - return cext_posix.getpriority(self.pid) - - @wrap_exceptions - def nice_set(self, value): - return cext_posix.setpriority(self.pid, value) - - # starting from CentOS 6. - if HAS_CPU_AFFINITY: - - @wrap_exceptions - def cpu_affinity_get(self): - return cext.proc_cpu_affinity_get(self.pid) - - def _get_eligible_cpus( - self, _re=re.compile(br"Cpus_allowed_list:\t(\d+)-(\d+)") - ): - # See: https://github.com/giampaolo/psutil/issues/956 - data = self._read_status_file() - match = _re.findall(data) - if match: - return list(range(int(match[0][0]), int(match[0][1]) + 1)) - else: - return list(range(len(per_cpu_times()))) - - @wrap_exceptions - def cpu_affinity_set(self, cpus): - try: - cext.proc_cpu_affinity_set(self.pid, cpus) - except (OSError, ValueError) as err: - if isinstance(err, ValueError) or err.errno == errno.EINVAL: - eligible_cpus = self._get_eligible_cpus() - all_cpus = tuple(range(len(per_cpu_times()))) - for cpu in cpus: - if cpu not in all_cpus: - msg = ( - f"invalid CPU {cpu!r}; choose between" - f" {eligible_cpus!r}" - ) - raise ValueError(msg) from None - if cpu not in eligible_cpus: - msg = ( - f"CPU number {cpu} is not eligible; choose" - f" between {eligible_cpus}" - ) - raise ValueError(msg) from err - raise - - # only starting from kernel 2.6.13 - if HAS_PROC_IO_PRIORITY: - - @wrap_exceptions - def ionice_get(self): - ioclass, value = cext.proc_ioprio_get(self.pid) - ioclass = IOPriority(ioclass) - return _common.pionice(ioclass, value) - - @wrap_exceptions - def ionice_set(self, ioclass, value): - if value is None: - value = 0 - if value and ioclass in { - IOPriority.IOPRIO_CLASS_IDLE, - IOPriority.IOPRIO_CLASS_NONE, - }: - msg = f"{ioclass!r} ioclass accepts no value" - raise ValueError(msg) - if value < 0 or value > 7: - msg = "value not in 0-7 range" - raise ValueError(msg) - return cext.proc_ioprio_set(self.pid, ioclass, value) - - if hasattr(resource, "prlimit"): - - @wrap_exceptions - def rlimit(self, resource_, limits=None): - # If pid is 0 prlimit() applies to the calling process and - # we don't want that. We should never get here though as - # PID 0 is not supported on Linux. - if self.pid == 0: - msg = "can't use prlimit() against PID 0 process" - raise ValueError(msg) - try: - if limits is None: - # get - return resource.prlimit(self.pid, resource_) - else: - # set - if len(limits) != 2: - msg = ( - "second argument must be a (soft, hard) " - f"tuple, got {limits!r}" - ) - raise ValueError(msg) - resource.prlimit(self.pid, resource_, limits) - except OSError as err: - if err.errno == errno.ENOSYS: - # I saw this happening on Travis: - # https://travis-ci.org/giampaolo/psutil/jobs/51368273 - self._raise_if_zombie() - raise - - @wrap_exceptions - def status(self): - letter = self._parse_stat_file()['status'] - letter = letter.decode() - # XXX is '?' legit? (we're not supposed to return it anyway) - return PROC_STATUSES.get(letter, '?') - - @wrap_exceptions - def open_files(self): - retlist = [] - files = os.listdir(f"{self._procfs_path}/{self.pid}/fd") - hit_enoent = False - for fd in files: - file = f"{self._procfs_path}/{self.pid}/fd/{fd}" - try: - path = readlink(file) - except (FileNotFoundError, ProcessLookupError): - # ENOENT == file which is gone in the meantime - hit_enoent = True - continue - except OSError as err: - if err.errno == errno.EINVAL: - # not a link - continue - if err.errno == errno.ENAMETOOLONG: - # file name too long - debug(err) - continue - raise - else: - # If path is not an absolute there's no way to tell - # whether it's a regular file or not, so we skip it. - # A regular file is always supposed to be have an - # absolute path though. - if path.startswith('/') and isfile_strict(path): - # Get file position and flags. - file = f"{self._procfs_path}/{self.pid}/fdinfo/{fd}" - try: - with open_binary(file) as f: - pos = int(f.readline().split()[1]) - flags = int(f.readline().split()[1], 8) - except (FileNotFoundError, ProcessLookupError): - # fd gone in the meantime; process may - # still be alive - hit_enoent = True - else: - mode = file_flags_to_mode(flags) - ntuple = popenfile( - path, int(fd), int(pos), mode, flags - ) - retlist.append(ntuple) - if hit_enoent: - self._raise_if_not_alive() - return retlist - - @wrap_exceptions - def net_connections(self, kind='inet'): - ret = _net_connections.retrieve(kind, self.pid) - self._raise_if_not_alive() - return ret - - @wrap_exceptions - def num_fds(self): - return len(os.listdir(f"{self._procfs_path}/{self.pid}/fd")) - - @wrap_exceptions - def ppid(self): - return int(self._parse_stat_file()['ppid']) - - @wrap_exceptions - def uids(self, _uids_re=re.compile(br'Uid:\t(\d+)\t(\d+)\t(\d+)')): - data = self._read_status_file() - real, effective, saved = _uids_re.findall(data)[0] - return _common.puids(int(real), int(effective), int(saved)) - - @wrap_exceptions - def gids(self, _gids_re=re.compile(br'Gid:\t(\d+)\t(\d+)\t(\d+)')): - data = self._read_status_file() - real, effective, saved = _gids_re.findall(data)[0] - return _common.pgids(int(real), int(effective), int(saved)) diff --git a/PortablePython/Lib/site-packages/psutil/_psosx.py b/PortablePython/Lib/site-packages/psutil/_psosx.py deleted file mode 100644 index 620497b..0000000 --- a/PortablePython/Lib/site-packages/psutil/_psosx.py +++ /dev/null @@ -1,544 +0,0 @@ -# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -"""macOS platform implementation.""" - -import errno -import functools -import os -from collections import namedtuple - -from . import _common -from . import _psposix -from . import _psutil_osx as cext -from . import _psutil_posix as cext_posix -from ._common import AccessDenied -from ._common import NoSuchProcess -from ._common import ZombieProcess -from ._common import conn_tmap -from ._common import conn_to_ntuple -from ._common import isfile_strict -from ._common import memoize_when_activated -from ._common import parse_environ_block -from ._common import usage_percent - - -__extra__all__ = [] - - -# ===================================================================== -# --- globals -# ===================================================================== - - -PAGESIZE = cext_posix.getpagesize() -AF_LINK = cext_posix.AF_LINK - -TCP_STATUSES = { - cext.TCPS_ESTABLISHED: _common.CONN_ESTABLISHED, - cext.TCPS_SYN_SENT: _common.CONN_SYN_SENT, - cext.TCPS_SYN_RECEIVED: _common.CONN_SYN_RECV, - cext.TCPS_FIN_WAIT_1: _common.CONN_FIN_WAIT1, - cext.TCPS_FIN_WAIT_2: _common.CONN_FIN_WAIT2, - cext.TCPS_TIME_WAIT: _common.CONN_TIME_WAIT, - cext.TCPS_CLOSED: _common.CONN_CLOSE, - cext.TCPS_CLOSE_WAIT: _common.CONN_CLOSE_WAIT, - cext.TCPS_LAST_ACK: _common.CONN_LAST_ACK, - cext.TCPS_LISTEN: _common.CONN_LISTEN, - cext.TCPS_CLOSING: _common.CONN_CLOSING, - cext.PSUTIL_CONN_NONE: _common.CONN_NONE, -} - -PROC_STATUSES = { - cext.SIDL: _common.STATUS_IDLE, - cext.SRUN: _common.STATUS_RUNNING, - cext.SSLEEP: _common.STATUS_SLEEPING, - cext.SSTOP: _common.STATUS_STOPPED, - cext.SZOMB: _common.STATUS_ZOMBIE, -} - -kinfo_proc_map = dict( - ppid=0, - ruid=1, - euid=2, - suid=3, - rgid=4, - egid=5, - sgid=6, - ttynr=7, - ctime=8, - status=9, - name=10, -) - -pidtaskinfo_map = dict( - cpuutime=0, - cpustime=1, - rss=2, - vms=3, - pfaults=4, - pageins=5, - numthreads=6, - volctxsw=7, -) - - -# ===================================================================== -# --- named tuples -# ===================================================================== - - -# fmt: off -# psutil.cpu_times() -scputimes = namedtuple('scputimes', ['user', 'nice', 'system', 'idle']) -# psutil.virtual_memory() -svmem = namedtuple( - 'svmem', ['total', 'available', 'percent', 'used', 'free', - 'active', 'inactive', 'wired']) -# psutil.Process.memory_info() -pmem = namedtuple('pmem', ['rss', 'vms', 'pfaults', 'pageins']) -# psutil.Process.memory_full_info() -pfullmem = namedtuple('pfullmem', pmem._fields + ('uss', )) -# fmt: on - - -# ===================================================================== -# --- memory -# ===================================================================== - - -def virtual_memory(): - """System virtual memory as a namedtuple.""" - total, active, inactive, wired, free, speculative = cext.virtual_mem() - # This is how Zabbix calculate avail and used mem: - # https://github.com/zabbix/zabbix/blob/master/src/libs/zbxsysinfo/osx/memory.c - # Also see: https://github.com/giampaolo/psutil/issues/1277 - avail = inactive + free - used = active + wired - # This is NOT how Zabbix calculates free mem but it matches "free" - # cmdline utility. - free -= speculative - percent = usage_percent((total - avail), total, round_=1) - return svmem(total, avail, percent, used, free, active, inactive, wired) - - -def swap_memory(): - """Swap system memory as a (total, used, free, sin, sout) tuple.""" - total, used, free, sin, sout = cext.swap_mem() - percent = usage_percent(used, total, round_=1) - return _common.sswap(total, used, free, percent, sin, sout) - - -# ===================================================================== -# --- CPU -# ===================================================================== - - -def cpu_times(): - """Return system CPU times as a namedtuple.""" - user, nice, system, idle = cext.cpu_times() - return scputimes(user, nice, system, idle) - - -def per_cpu_times(): - """Return system CPU times as a named tuple.""" - ret = [] - for cpu_t in cext.per_cpu_times(): - user, nice, system, idle = cpu_t - item = scputimes(user, nice, system, idle) - ret.append(item) - return ret - - -def cpu_count_logical(): - """Return the number of logical CPUs in the system.""" - return cext.cpu_count_logical() - - -def cpu_count_cores(): - """Return the number of CPU cores in the system.""" - return cext.cpu_count_cores() - - -def cpu_stats(): - ctx_switches, interrupts, soft_interrupts, syscalls, _traps = ( - cext.cpu_stats() - ) - return _common.scpustats( - ctx_switches, interrupts, soft_interrupts, syscalls - ) - - -def cpu_freq(): - """Return CPU frequency. - On macOS per-cpu frequency is not supported. - Also, the returned frequency never changes, see: - https://arstechnica.com/civis/viewtopic.php?f=19&t=465002. - """ - curr, min_, max_ = cext.cpu_freq() - return [_common.scpufreq(curr, min_, max_)] - - -# ===================================================================== -# --- disks -# ===================================================================== - - -disk_usage = _psposix.disk_usage -disk_io_counters = cext.disk_io_counters - - -def disk_partitions(all=False): - """Return mounted disk partitions as a list of namedtuples.""" - retlist = [] - partitions = cext.disk_partitions() - for partition in partitions: - device, mountpoint, fstype, opts = partition - if device == 'none': - device = '' - if not all: - if not os.path.isabs(device) or not os.path.exists(device): - continue - ntuple = _common.sdiskpart(device, mountpoint, fstype, opts) - retlist.append(ntuple) - return retlist - - -# ===================================================================== -# --- sensors -# ===================================================================== - - -def sensors_battery(): - """Return battery information.""" - try: - percent, minsleft, power_plugged = cext.sensors_battery() - except NotImplementedError: - # no power source - return None according to interface - return None - power_plugged = power_plugged == 1 - if power_plugged: - secsleft = _common.POWER_TIME_UNLIMITED - elif minsleft == -1: - secsleft = _common.POWER_TIME_UNKNOWN - else: - secsleft = minsleft * 60 - return _common.sbattery(percent, secsleft, power_plugged) - - -# ===================================================================== -# --- network -# ===================================================================== - - -net_io_counters = cext.net_io_counters -net_if_addrs = cext_posix.net_if_addrs - - -def net_connections(kind='inet'): - """System-wide network connections.""" - # Note: on macOS this will fail with AccessDenied unless - # the process is owned by root. - ret = [] - for pid in pids(): - try: - cons = Process(pid).net_connections(kind) - except NoSuchProcess: - continue - else: - if cons: - for c in cons: - c = list(c) + [pid] - ret.append(_common.sconn(*c)) - return ret - - -def net_if_stats(): - """Get NIC stats (isup, duplex, speed, mtu).""" - names = net_io_counters().keys() - ret = {} - for name in names: - try: - mtu = cext_posix.net_if_mtu(name) - flags = cext_posix.net_if_flags(name) - duplex, speed = cext_posix.net_if_duplex_speed(name) - except OSError as err: - # https://github.com/giampaolo/psutil/issues/1279 - if err.errno != errno.ENODEV: - raise - else: - if hasattr(_common, 'NicDuplex'): - duplex = _common.NicDuplex(duplex) - output_flags = ','.join(flags) - isup = 'running' in flags - ret[name] = _common.snicstats( - isup, duplex, speed, mtu, output_flags - ) - return ret - - -# ===================================================================== -# --- other system functions -# ===================================================================== - - -def boot_time(): - """The system boot time expressed in seconds since the epoch.""" - return cext.boot_time() - - -def users(): - """Return currently connected users as a list of namedtuples.""" - retlist = [] - rawlist = cext.users() - for item in rawlist: - user, tty, hostname, tstamp, pid = item - if tty == '~': - continue # reboot or shutdown - if not tstamp: - continue - nt = _common.suser(user, tty or None, hostname or None, tstamp, pid) - retlist.append(nt) - return retlist - - -# ===================================================================== -# --- processes -# ===================================================================== - - -def pids(): - ls = cext.pids() - if 0 not in ls: - # On certain macOS versions pids() C doesn't return PID 0 but - # "ps" does and the process is querable via sysctl(): - # https://travis-ci.org/giampaolo/psutil/jobs/309619941 - try: - Process(0).create_time() - ls.insert(0, 0) - except NoSuchProcess: - pass - except AccessDenied: - ls.insert(0, 0) - return ls - - -pid_exists = _psposix.pid_exists - - -def is_zombie(pid): - try: - st = cext.proc_kinfo_oneshot(pid)[kinfo_proc_map['status']] - return st == cext.SZOMB - except OSError: - return False - - -def wrap_exceptions(fun): - """Decorator which translates bare OSError exceptions into - NoSuchProcess and AccessDenied. - """ - - @functools.wraps(fun) - def wrapper(self, *args, **kwargs): - pid, ppid, name = self.pid, self._ppid, self._name - try: - return fun(self, *args, **kwargs) - except ProcessLookupError as err: - if is_zombie(pid): - raise ZombieProcess(pid, name, ppid) from err - raise NoSuchProcess(pid, name) from err - except PermissionError as err: - raise AccessDenied(pid, name) from err - - return wrapper - - -class Process: - """Wrapper class around underlying C implementation.""" - - __slots__ = ["_cache", "_name", "_ppid", "pid"] - - def __init__(self, pid): - self.pid = pid - self._name = None - self._ppid = None - - @wrap_exceptions - @memoize_when_activated - def _get_kinfo_proc(self): - # Note: should work with all PIDs without permission issues. - ret = cext.proc_kinfo_oneshot(self.pid) - assert len(ret) == len(kinfo_proc_map) - return ret - - @wrap_exceptions - @memoize_when_activated - def _get_pidtaskinfo(self): - # Note: should work for PIDs owned by user only. - ret = cext.proc_pidtaskinfo_oneshot(self.pid) - assert len(ret) == len(pidtaskinfo_map) - return ret - - def oneshot_enter(self): - self._get_kinfo_proc.cache_activate(self) - self._get_pidtaskinfo.cache_activate(self) - - def oneshot_exit(self): - self._get_kinfo_proc.cache_deactivate(self) - self._get_pidtaskinfo.cache_deactivate(self) - - @wrap_exceptions - def name(self): - name = self._get_kinfo_proc()[kinfo_proc_map['name']] - return name if name is not None else cext.proc_name(self.pid) - - @wrap_exceptions - def exe(self): - return cext.proc_exe(self.pid) - - @wrap_exceptions - def cmdline(self): - return cext.proc_cmdline(self.pid) - - @wrap_exceptions - def environ(self): - return parse_environ_block(cext.proc_environ(self.pid)) - - @wrap_exceptions - def ppid(self): - self._ppid = self._get_kinfo_proc()[kinfo_proc_map['ppid']] - return self._ppid - - @wrap_exceptions - def cwd(self): - return cext.proc_cwd(self.pid) - - @wrap_exceptions - def uids(self): - rawtuple = self._get_kinfo_proc() - return _common.puids( - rawtuple[kinfo_proc_map['ruid']], - rawtuple[kinfo_proc_map['euid']], - rawtuple[kinfo_proc_map['suid']], - ) - - @wrap_exceptions - def gids(self): - rawtuple = self._get_kinfo_proc() - return _common.puids( - rawtuple[kinfo_proc_map['rgid']], - rawtuple[kinfo_proc_map['egid']], - rawtuple[kinfo_proc_map['sgid']], - ) - - @wrap_exceptions - def terminal(self): - tty_nr = self._get_kinfo_proc()[kinfo_proc_map['ttynr']] - tmap = _psposix.get_terminal_map() - try: - return tmap[tty_nr] - except KeyError: - return None - - @wrap_exceptions - def memory_info(self): - rawtuple = self._get_pidtaskinfo() - return pmem( - rawtuple[pidtaskinfo_map['rss']], - rawtuple[pidtaskinfo_map['vms']], - rawtuple[pidtaskinfo_map['pfaults']], - rawtuple[pidtaskinfo_map['pageins']], - ) - - @wrap_exceptions - def memory_full_info(self): - basic_mem = self.memory_info() - uss = cext.proc_memory_uss(self.pid) - return pfullmem(*basic_mem + (uss,)) - - @wrap_exceptions - def cpu_times(self): - rawtuple = self._get_pidtaskinfo() - return _common.pcputimes( - rawtuple[pidtaskinfo_map['cpuutime']], - rawtuple[pidtaskinfo_map['cpustime']], - # children user / system times are not retrievable (set to 0) - 0.0, - 0.0, - ) - - @wrap_exceptions - def create_time(self): - return self._get_kinfo_proc()[kinfo_proc_map['ctime']] - - @wrap_exceptions - def num_ctx_switches(self): - # Unvoluntary value seems not to be available; - # getrusage() numbers seems to confirm this theory. - # We set it to 0. - vol = self._get_pidtaskinfo()[pidtaskinfo_map['volctxsw']] - return _common.pctxsw(vol, 0) - - @wrap_exceptions - def num_threads(self): - return self._get_pidtaskinfo()[pidtaskinfo_map['numthreads']] - - @wrap_exceptions - def open_files(self): - if self.pid == 0: - return [] - files = [] - rawlist = cext.proc_open_files(self.pid) - for path, fd in rawlist: - if isfile_strict(path): - ntuple = _common.popenfile(path, fd) - files.append(ntuple) - return files - - @wrap_exceptions - def net_connections(self, kind='inet'): - families, types = conn_tmap[kind] - rawlist = cext.proc_net_connections(self.pid, families, types) - ret = [] - for item in rawlist: - fd, fam, type, laddr, raddr, status = item - nt = conn_to_ntuple( - fd, fam, type, laddr, raddr, status, TCP_STATUSES - ) - ret.append(nt) - return ret - - @wrap_exceptions - def num_fds(self): - if self.pid == 0: - return 0 - return cext.proc_num_fds(self.pid) - - @wrap_exceptions - def wait(self, timeout=None): - return _psposix.wait_pid(self.pid, timeout, self._name) - - @wrap_exceptions - def nice_get(self): - return cext_posix.getpriority(self.pid) - - @wrap_exceptions - def nice_set(self, value): - return cext_posix.setpriority(self.pid, value) - - @wrap_exceptions - def status(self): - code = self._get_kinfo_proc()[kinfo_proc_map['status']] - # XXX is '?' legit? (we're not supposed to return it anyway) - return PROC_STATUSES.get(code, '?') - - @wrap_exceptions - def threads(self): - rawlist = cext.proc_threads(self.pid) - retlist = [] - for thread_id, utime, stime in rawlist: - ntuple = _common.pthread(thread_id, utime, stime) - retlist.append(ntuple) - return retlist diff --git a/PortablePython/Lib/site-packages/psutil/_psposix.py b/PortablePython/Lib/site-packages/psutil/_psposix.py deleted file mode 100644 index 88703fd..0000000 --- a/PortablePython/Lib/site-packages/psutil/_psposix.py +++ /dev/null @@ -1,207 +0,0 @@ -# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -"""Routines common to all posix systems.""" - -import enum -import glob -import os -import signal -import time - -from ._common import MACOS -from ._common import TimeoutExpired -from ._common import memoize -from ._common import sdiskusage -from ._common import usage_percent - - -if MACOS: - from . import _psutil_osx - - -__all__ = ['pid_exists', 'wait_pid', 'disk_usage', 'get_terminal_map'] - - -def pid_exists(pid): - """Check whether pid exists in the current process table.""" - if pid == 0: - # According to "man 2 kill" PID 0 has a special meaning: - # it refers to <> so we don't want to go any further. - # If we get here it means this UNIX platform *does* have - # a process with id 0. - return True - try: - os.kill(pid, 0) - except ProcessLookupError: - return False - except PermissionError: - # EPERM clearly means there's a process to deny access to - return True - # According to "man 2 kill" possible error values are - # (EINVAL, EPERM, ESRCH) - else: - return True - - -Negsignal = enum.IntEnum( - 'Negsignal', {x.name: -x.value for x in signal.Signals} -) - - -def negsig_to_enum(num): - """Convert a negative signal value to an enum.""" - try: - return Negsignal(num) - except ValueError: - return num - - -def wait_pid( - pid, - timeout=None, - proc_name=None, - _waitpid=os.waitpid, - _timer=getattr(time, 'monotonic', time.time), # noqa: B008 - _min=min, - _sleep=time.sleep, - _pid_exists=pid_exists, -): - """Wait for a process PID to terminate. - - If the process terminated normally by calling exit(3) or _exit(2), - or by returning from main(), the return value is the positive integer - passed to *exit(). - - If it was terminated by a signal it returns the negated value of the - signal which caused the termination (e.g. -SIGTERM). - - If PID is not a children of os.getpid() (current process) just - wait until the process disappears and return None. - - If PID does not exist at all return None immediately. - - If *timeout* != None and process is still alive raise TimeoutExpired. - timeout=0 is also possible (either return immediately or raise). - """ - if pid <= 0: - # see "man waitpid" - msg = "can't wait for PID 0" - raise ValueError(msg) - interval = 0.0001 - flags = 0 - if timeout is not None: - flags |= os.WNOHANG - stop_at = _timer() + timeout - - def sleep(interval): - # Sleep for some time and return a new increased interval. - if timeout is not None: - if _timer() >= stop_at: - raise TimeoutExpired(timeout, pid=pid, name=proc_name) - _sleep(interval) - return _min(interval * 2, 0.04) - - # See: https://linux.die.net/man/2/waitpid - while True: - try: - retpid, status = os.waitpid(pid, flags) - except InterruptedError: - interval = sleep(interval) - except ChildProcessError: - # This has two meanings: - # - PID is not a child of os.getpid() in which case - # we keep polling until it's gone - # - PID never existed in the first place - # In both cases we'll eventually return None as we - # can't determine its exit status code. - while _pid_exists(pid): - interval = sleep(interval) - return None - else: - if retpid == 0: - # WNOHANG flag was used and PID is still running. - interval = sleep(interval) - continue - - if os.WIFEXITED(status): - # Process terminated normally by calling exit(3) or _exit(2), - # or by returning from main(). The return value is the - # positive integer passed to *exit(). - return os.WEXITSTATUS(status) - elif os.WIFSIGNALED(status): - # Process exited due to a signal. Return the negative value - # of that signal. - return negsig_to_enum(-os.WTERMSIG(status)) - # elif os.WIFSTOPPED(status): - # # Process was stopped via SIGSTOP or is being traced, and - # # waitpid() was called with WUNTRACED flag. PID is still - # # alive. From now on waitpid() will keep returning (0, 0) - # # until the process state doesn't change. - # # It may make sense to catch/enable this since stopped PIDs - # # ignore SIGTERM. - # interval = sleep(interval) - # continue - # elif os.WIFCONTINUED(status): - # # Process was resumed via SIGCONT and waitpid() was called - # # with WCONTINUED flag. - # interval = sleep(interval) - # continue - else: - # Should never happen. - msg = f"unknown process exit status {status!r}" - raise ValueError(msg) - - -def disk_usage(path): - """Return disk usage associated with path. - Note: UNIX usually reserves 5% disk space which is not accessible - by user. In this function "total" and "used" values reflect the - total and used disk space whereas "free" and "percent" represent - the "free" and "used percent" user disk space. - """ - st = os.statvfs(path) - # Total space which is only available to root (unless changed - # at system level). - total = st.f_blocks * st.f_frsize - # Remaining free space usable by root. - avail_to_root = st.f_bfree * st.f_frsize - # Remaining free space usable by user. - avail_to_user = st.f_bavail * st.f_frsize - # Total space being used in general. - used = total - avail_to_root - if MACOS: - # see: https://github.com/giampaolo/psutil/pull/2152 - used = _psutil_osx.disk_usage_used(path, used) - # Total space which is available to user (same as 'total' but - # for the user). - total_user = used + avail_to_user - # User usage percent compared to the total amount of space - # the user can use. This number would be higher if compared - # to root's because the user has less space (usually -5%). - usage_percent_user = usage_percent(used, total_user, round_=1) - - # NB: the percentage is -5% than what shown by df due to - # reserved blocks that we are currently not considering: - # https://github.com/giampaolo/psutil/issues/829#issuecomment-223750462 - return sdiskusage( - total=total, used=used, free=avail_to_user, percent=usage_percent_user - ) - - -@memoize -def get_terminal_map(): - """Get a map of device-id -> path as a dict. - Used by Process.terminal(). - """ - ret = {} - ls = glob.glob('/dev/tty*') + glob.glob('/dev/pts/*') - for name in ls: - assert name not in ret, name - try: - ret[os.stat(name).st_rdev] = name - except FileNotFoundError: - pass - return ret diff --git a/PortablePython/Lib/site-packages/psutil/_pssunos.py b/PortablePython/Lib/site-packages/psutil/_pssunos.py deleted file mode 100644 index 78d941c..0000000 --- a/PortablePython/Lib/site-packages/psutil/_pssunos.py +++ /dev/null @@ -1,734 +0,0 @@ -# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -"""Sun OS Solaris platform implementation.""" - -import errno -import functools -import os -import socket -import subprocess -import sys -from collections import namedtuple -from socket import AF_INET - -from . import _common -from . import _psposix -from . import _psutil_posix as cext_posix -from . import _psutil_sunos as cext -from ._common import AF_INET6 -from ._common import ENCODING -from ._common import AccessDenied -from ._common import NoSuchProcess -from ._common import ZombieProcess -from ._common import debug -from ._common import get_procfs_path -from ._common import isfile_strict -from ._common import memoize_when_activated -from ._common import sockfam_to_enum -from ._common import socktype_to_enum -from ._common import usage_percent - - -__extra__all__ = ["CONN_IDLE", "CONN_BOUND", "PROCFS_PATH"] - - -# ===================================================================== -# --- globals -# ===================================================================== - - -PAGE_SIZE = cext_posix.getpagesize() -AF_LINK = cext_posix.AF_LINK -IS_64_BIT = sys.maxsize > 2**32 - -CONN_IDLE = "IDLE" -CONN_BOUND = "BOUND" - -PROC_STATUSES = { - cext.SSLEEP: _common.STATUS_SLEEPING, - cext.SRUN: _common.STATUS_RUNNING, - cext.SZOMB: _common.STATUS_ZOMBIE, - cext.SSTOP: _common.STATUS_STOPPED, - cext.SIDL: _common.STATUS_IDLE, - cext.SONPROC: _common.STATUS_RUNNING, # same as run - cext.SWAIT: _common.STATUS_WAITING, -} - -TCP_STATUSES = { - cext.TCPS_ESTABLISHED: _common.CONN_ESTABLISHED, - cext.TCPS_SYN_SENT: _common.CONN_SYN_SENT, - cext.TCPS_SYN_RCVD: _common.CONN_SYN_RECV, - cext.TCPS_FIN_WAIT_1: _common.CONN_FIN_WAIT1, - cext.TCPS_FIN_WAIT_2: _common.CONN_FIN_WAIT2, - cext.TCPS_TIME_WAIT: _common.CONN_TIME_WAIT, - cext.TCPS_CLOSED: _common.CONN_CLOSE, - cext.TCPS_CLOSE_WAIT: _common.CONN_CLOSE_WAIT, - cext.TCPS_LAST_ACK: _common.CONN_LAST_ACK, - cext.TCPS_LISTEN: _common.CONN_LISTEN, - cext.TCPS_CLOSING: _common.CONN_CLOSING, - cext.PSUTIL_CONN_NONE: _common.CONN_NONE, - cext.TCPS_IDLE: CONN_IDLE, # sunos specific - cext.TCPS_BOUND: CONN_BOUND, # sunos specific -} - -proc_info_map = dict( - ppid=0, - rss=1, - vms=2, - create_time=3, - nice=4, - num_threads=5, - status=6, - ttynr=7, - uid=8, - euid=9, - gid=10, - egid=11, -) - - -# ===================================================================== -# --- named tuples -# ===================================================================== - - -# psutil.cpu_times() -scputimes = namedtuple('scputimes', ['user', 'system', 'idle', 'iowait']) -# psutil.cpu_times(percpu=True) -pcputimes = namedtuple( - 'pcputimes', ['user', 'system', 'children_user', 'children_system'] -) -# psutil.virtual_memory() -svmem = namedtuple('svmem', ['total', 'available', 'percent', 'used', 'free']) -# psutil.Process.memory_info() -pmem = namedtuple('pmem', ['rss', 'vms']) -pfullmem = pmem -# psutil.Process.memory_maps(grouped=True) -pmmap_grouped = namedtuple( - 'pmmap_grouped', ['path', 'rss', 'anonymous', 'locked'] -) -# psutil.Process.memory_maps(grouped=False) -pmmap_ext = namedtuple( - 'pmmap_ext', 'addr perms ' + ' '.join(pmmap_grouped._fields) -) - - -# ===================================================================== -# --- memory -# ===================================================================== - - -def virtual_memory(): - """Report virtual memory metrics.""" - # we could have done this with kstat, but IMHO this is good enough - total = os.sysconf('SC_PHYS_PAGES') * PAGE_SIZE - # note: there's no difference on Solaris - free = avail = os.sysconf('SC_AVPHYS_PAGES') * PAGE_SIZE - used = total - free - percent = usage_percent(used, total, round_=1) - return svmem(total, avail, percent, used, free) - - -def swap_memory(): - """Report swap memory metrics.""" - sin, sout = cext.swap_mem() - # XXX - # we are supposed to get total/free by doing so: - # http://cvs.opensolaris.org/source/xref/onnv/onnv-gate/ - # usr/src/cmd/swap/swap.c - # ...nevertheless I can't manage to obtain the same numbers as 'swap' - # cmdline utility, so let's parse its output (sigh!) - p = subprocess.Popen( - [ - '/usr/bin/env', - f"PATH=/usr/sbin:/sbin:{os.environ['PATH']}", - 'swap', - '-l', - ], - stdout=subprocess.PIPE, - ) - stdout, _ = p.communicate() - stdout = stdout.decode(sys.stdout.encoding) - if p.returncode != 0: - msg = f"'swap -l' failed (retcode={p.returncode})" - raise RuntimeError(msg) - - lines = stdout.strip().split('\n')[1:] - if not lines: - msg = 'no swap device(s) configured' - raise RuntimeError(msg) - total = free = 0 - for line in lines: - line = line.split() - t, f = line[3:5] - total += int(int(t) * 512) - free += int(int(f) * 512) - used = total - free - percent = usage_percent(used, total, round_=1) - return _common.sswap( - total, used, free, percent, sin * PAGE_SIZE, sout * PAGE_SIZE - ) - - -# ===================================================================== -# --- CPU -# ===================================================================== - - -def cpu_times(): - """Return system-wide CPU times as a named tuple.""" - ret = cext.per_cpu_times() - return scputimes(*[sum(x) for x in zip(*ret)]) - - -def per_cpu_times(): - """Return system per-CPU times as a list of named tuples.""" - ret = cext.per_cpu_times() - return [scputimes(*x) for x in ret] - - -def cpu_count_logical(): - """Return the number of logical CPUs in the system.""" - try: - return os.sysconf("SC_NPROCESSORS_ONLN") - except ValueError: - # mimic os.cpu_count() behavior - return None - - -def cpu_count_cores(): - """Return the number of CPU cores in the system.""" - return cext.cpu_count_cores() - - -def cpu_stats(): - """Return various CPU stats as a named tuple.""" - ctx_switches, interrupts, syscalls, _traps = cext.cpu_stats() - soft_interrupts = 0 - return _common.scpustats( - ctx_switches, interrupts, soft_interrupts, syscalls - ) - - -# ===================================================================== -# --- disks -# ===================================================================== - - -disk_io_counters = cext.disk_io_counters -disk_usage = _psposix.disk_usage - - -def disk_partitions(all=False): - """Return system disk partitions.""" - # TODO - the filtering logic should be better checked so that - # it tries to reflect 'df' as much as possible - retlist = [] - partitions = cext.disk_partitions() - for partition in partitions: - device, mountpoint, fstype, opts = partition - if device == 'none': - device = '' - if not all: - # Differently from, say, Linux, we don't have a list of - # common fs types so the best we can do, AFAIK, is to - # filter by filesystem having a total size > 0. - try: - if not disk_usage(mountpoint).total: - continue - except OSError as err: - # https://github.com/giampaolo/psutil/issues/1674 - debug(f"skipping {mountpoint!r}: {err}") - continue - ntuple = _common.sdiskpart(device, mountpoint, fstype, opts) - retlist.append(ntuple) - return retlist - - -# ===================================================================== -# --- network -# ===================================================================== - - -net_io_counters = cext.net_io_counters -net_if_addrs = cext_posix.net_if_addrs - - -def net_connections(kind, _pid=-1): - """Return socket connections. If pid == -1 return system-wide - connections (as opposed to connections opened by one process only). - Only INET sockets are returned (UNIX are not). - """ - families, types = _common.conn_tmap[kind] - rawlist = cext.net_connections(_pid) - ret = set() - for item in rawlist: - fd, fam, type_, laddr, raddr, status, pid = item - if fam not in families: - continue - if type_ not in types: - continue - # TODO: refactor and use _common.conn_to_ntuple. - if fam in {AF_INET, AF_INET6}: - if laddr: - laddr = _common.addr(*laddr) - if raddr: - raddr = _common.addr(*raddr) - status = TCP_STATUSES[status] - fam = sockfam_to_enum(fam) - type_ = socktype_to_enum(type_) - if _pid == -1: - nt = _common.sconn(fd, fam, type_, laddr, raddr, status, pid) - else: - nt = _common.pconn(fd, fam, type_, laddr, raddr, status) - ret.add(nt) - return list(ret) - - -def net_if_stats(): - """Get NIC stats (isup, duplex, speed, mtu).""" - ret = cext.net_if_stats() - for name, items in ret.items(): - isup, duplex, speed, mtu = items - if hasattr(_common, 'NicDuplex'): - duplex = _common.NicDuplex(duplex) - ret[name] = _common.snicstats(isup, duplex, speed, mtu, '') - return ret - - -# ===================================================================== -# --- other system functions -# ===================================================================== - - -def boot_time(): - """The system boot time expressed in seconds since the epoch.""" - return cext.boot_time() - - -def users(): - """Return currently connected users as a list of namedtuples.""" - retlist = [] - rawlist = cext.users() - localhost = (':0.0', ':0') - for item in rawlist: - user, tty, hostname, tstamp, user_process, pid = item - # note: the underlying C function includes entries about - # system boot, run level and others. We might want - # to use them in the future. - if not user_process: - continue - if hostname in localhost: - hostname = 'localhost' - nt = _common.suser(user, tty, hostname, tstamp, pid) - retlist.append(nt) - return retlist - - -# ===================================================================== -# --- processes -# ===================================================================== - - -def pids(): - """Returns a list of PIDs currently running on the system.""" - path = get_procfs_path().encode(ENCODING) - return [int(x) for x in os.listdir(path) if x.isdigit()] - - -def pid_exists(pid): - """Check for the existence of a unix pid.""" - return _psposix.pid_exists(pid) - - -def wrap_exceptions(fun): - """Call callable into a try/except clause and translate ENOENT, - EACCES and EPERM in NoSuchProcess or AccessDenied exceptions. - """ - - @functools.wraps(fun) - def wrapper(self, *args, **kwargs): - pid, ppid, name = self.pid, self._ppid, self._name - try: - return fun(self, *args, **kwargs) - except (FileNotFoundError, ProcessLookupError) as err: - # ENOENT (no such file or directory) gets raised on open(). - # ESRCH (no such process) can get raised on read() if - # process is gone in meantime. - if not pid_exists(pid): - raise NoSuchProcess(pid, name) from err - raise ZombieProcess(pid, name, ppid) from err - except PermissionError as err: - raise AccessDenied(pid, name) from err - except OSError as err: - if pid == 0: - if 0 in pids(): - raise AccessDenied(pid, name) from err - raise - raise - - return wrapper - - -class Process: - """Wrapper class around underlying C implementation.""" - - __slots__ = ["_cache", "_name", "_ppid", "_procfs_path", "pid"] - - def __init__(self, pid): - self.pid = pid - self._name = None - self._ppid = None - self._procfs_path = get_procfs_path() - - def _assert_alive(self): - """Raise NSP if the process disappeared on us.""" - # For those C function who do not raise NSP, possibly returning - # incorrect or incomplete result. - os.stat(f"{self._procfs_path}/{self.pid}") - - def oneshot_enter(self): - self._proc_name_and_args.cache_activate(self) - self._proc_basic_info.cache_activate(self) - self._proc_cred.cache_activate(self) - - def oneshot_exit(self): - self._proc_name_and_args.cache_deactivate(self) - self._proc_basic_info.cache_deactivate(self) - self._proc_cred.cache_deactivate(self) - - @wrap_exceptions - @memoize_when_activated - def _proc_name_and_args(self): - return cext.proc_name_and_args(self.pid, self._procfs_path) - - @wrap_exceptions - @memoize_when_activated - def _proc_basic_info(self): - if self.pid == 0 and not os.path.exists( - f"{self._procfs_path}/{self.pid}/psinfo" - ): - raise AccessDenied(self.pid) - ret = cext.proc_basic_info(self.pid, self._procfs_path) - assert len(ret) == len(proc_info_map) - return ret - - @wrap_exceptions - @memoize_when_activated - def _proc_cred(self): - return cext.proc_cred(self.pid, self._procfs_path) - - @wrap_exceptions - def name(self): - # note: max len == 15 - return self._proc_name_and_args()[0] - - @wrap_exceptions - def exe(self): - try: - return os.readlink(f"{self._procfs_path}/{self.pid}/path/a.out") - except OSError: - pass # continue and guess the exe name from the cmdline - # Will be guessed later from cmdline but we want to explicitly - # invoke cmdline here in order to get an AccessDenied - # exception if the user has not enough privileges. - self.cmdline() - return "" - - @wrap_exceptions - def cmdline(self): - return self._proc_name_and_args()[1].split(' ') - - @wrap_exceptions - def environ(self): - return cext.proc_environ(self.pid, self._procfs_path) - - @wrap_exceptions - def create_time(self): - return self._proc_basic_info()[proc_info_map['create_time']] - - @wrap_exceptions - def num_threads(self): - return self._proc_basic_info()[proc_info_map['num_threads']] - - @wrap_exceptions - def nice_get(self): - # Note #1: getpriority(3) doesn't work for realtime processes. - # Psinfo is what ps uses, see: - # https://github.com/giampaolo/psutil/issues/1194 - return self._proc_basic_info()[proc_info_map['nice']] - - @wrap_exceptions - def nice_set(self, value): - if self.pid in {2, 3}: - # Special case PIDs: internally setpriority(3) return ESRCH - # (no such process), no matter what. - # The process actually exists though, as it has a name, - # creation time, etc. - raise AccessDenied(self.pid, self._name) - return cext_posix.setpriority(self.pid, value) - - @wrap_exceptions - def ppid(self): - self._ppid = self._proc_basic_info()[proc_info_map['ppid']] - return self._ppid - - @wrap_exceptions - def uids(self): - try: - real, effective, saved, _, _, _ = self._proc_cred() - except AccessDenied: - real = self._proc_basic_info()[proc_info_map['uid']] - effective = self._proc_basic_info()[proc_info_map['euid']] - saved = None - return _common.puids(real, effective, saved) - - @wrap_exceptions - def gids(self): - try: - _, _, _, real, effective, saved = self._proc_cred() - except AccessDenied: - real = self._proc_basic_info()[proc_info_map['gid']] - effective = self._proc_basic_info()[proc_info_map['egid']] - saved = None - return _common.puids(real, effective, saved) - - @wrap_exceptions - def cpu_times(self): - try: - times = cext.proc_cpu_times(self.pid, self._procfs_path) - except OSError as err: - if err.errno == errno.EOVERFLOW and not IS_64_BIT: - # We may get here if we attempt to query a 64bit process - # with a 32bit python. - # Error originates from read() and also tools like "cat" - # fail in the same way (!). - # Since there simply is no way to determine CPU times we - # return 0.0 as a fallback. See: - # https://github.com/giampaolo/psutil/issues/857 - times = (0.0, 0.0, 0.0, 0.0) - else: - raise - return _common.pcputimes(*times) - - @wrap_exceptions - def cpu_num(self): - return cext.proc_cpu_num(self.pid, self._procfs_path) - - @wrap_exceptions - def terminal(self): - procfs_path = self._procfs_path - hit_enoent = False - tty = wrap_exceptions(self._proc_basic_info()[proc_info_map['ttynr']]) - if tty != cext.PRNODEV: - for x in (0, 1, 2, 255): - try: - return os.readlink(f"{procfs_path}/{self.pid}/path/{x}") - except FileNotFoundError: - hit_enoent = True - continue - if hit_enoent: - self._assert_alive() - - @wrap_exceptions - def cwd(self): - # /proc/PID/path/cwd may not be resolved by readlink() even if - # it exists (ls shows it). If that's the case and the process - # is still alive return None (we can return None also on BSD). - # Reference: https://groups.google.com/g/comp.unix.solaris/c/tcqvhTNFCAs - procfs_path = self._procfs_path - try: - return os.readlink(f"{procfs_path}/{self.pid}/path/cwd") - except FileNotFoundError: - os.stat(f"{procfs_path}/{self.pid}") # raise NSP or AD - return "" - - @wrap_exceptions - def memory_info(self): - ret = self._proc_basic_info() - rss = ret[proc_info_map['rss']] * 1024 - vms = ret[proc_info_map['vms']] * 1024 - return pmem(rss, vms) - - memory_full_info = memory_info - - @wrap_exceptions - def status(self): - code = self._proc_basic_info()[proc_info_map['status']] - # XXX is '?' legit? (we're not supposed to return it anyway) - return PROC_STATUSES.get(code, '?') - - @wrap_exceptions - def threads(self): - procfs_path = self._procfs_path - ret = [] - tids = os.listdir(f"{procfs_path}/{self.pid}/lwp") - hit_enoent = False - for tid in tids: - tid = int(tid) - try: - utime, stime = cext.query_process_thread( - self.pid, tid, procfs_path - ) - except OSError as err: - if err.errno == errno.EOVERFLOW and not IS_64_BIT: - # We may get here if we attempt to query a 64bit process - # with a 32bit python. - # Error originates from read() and also tools like "cat" - # fail in the same way (!). - # Since there simply is no way to determine CPU times we - # return 0.0 as a fallback. See: - # https://github.com/giampaolo/psutil/issues/857 - continue - # ENOENT == thread gone in meantime - if err.errno == errno.ENOENT: - hit_enoent = True - continue - raise - else: - nt = _common.pthread(tid, utime, stime) - ret.append(nt) - if hit_enoent: - self._assert_alive() - return ret - - @wrap_exceptions - def open_files(self): - retlist = [] - hit_enoent = False - procfs_path = self._procfs_path - pathdir = f"{procfs_path}/{self.pid}/path" - for fd in os.listdir(f"{procfs_path}/{self.pid}/fd"): - path = os.path.join(pathdir, fd) - if os.path.islink(path): - try: - file = os.readlink(path) - except FileNotFoundError: - hit_enoent = True - continue - else: - if isfile_strict(file): - retlist.append(_common.popenfile(file, int(fd))) - if hit_enoent: - self._assert_alive() - return retlist - - def _get_unix_sockets(self, pid): - """Get UNIX sockets used by process by parsing 'pfiles' output.""" - # TODO: rewrite this in C (...but the damn netstat source code - # does not include this part! Argh!!) - cmd = ["pfiles", str(pid)] - p = subprocess.Popen( - cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE - ) - stdout, stderr = p.communicate() - stdout, stderr = ( - x.decode(sys.stdout.encoding) for x in (stdout, stderr) - ) - if p.returncode != 0: - if 'permission denied' in stderr.lower(): - raise AccessDenied(self.pid, self._name) - if 'no such process' in stderr.lower(): - raise NoSuchProcess(self.pid, self._name) - msg = f"{cmd!r} command error\n{stderr}" - raise RuntimeError(msg) - - lines = stdout.split('\n')[2:] - for i, line in enumerate(lines): - line = line.lstrip() - if line.startswith('sockname: AF_UNIX'): - path = line.split(' ', 2)[2] - type = lines[i - 2].strip() - if type == 'SOCK_STREAM': - type = socket.SOCK_STREAM - elif type == 'SOCK_DGRAM': - type = socket.SOCK_DGRAM - else: - type = -1 - yield (-1, socket.AF_UNIX, type, path, "", _common.CONN_NONE) - - @wrap_exceptions - def net_connections(self, kind='inet'): - ret = net_connections(kind, _pid=self.pid) - # The underlying C implementation retrieves all OS connections - # and filters them by PID. At this point we can't tell whether - # an empty list means there were no connections for process or - # process is no longer active so we force NSP in case the PID - # is no longer there. - if not ret: - # will raise NSP if process is gone - os.stat(f"{self._procfs_path}/{self.pid}") - - # UNIX sockets - if kind in {'all', 'unix'}: - ret.extend([ - _common.pconn(*conn) - for conn in self._get_unix_sockets(self.pid) - ]) - return ret - - nt_mmap_grouped = namedtuple('mmap', 'path rss anon locked') - nt_mmap_ext = namedtuple('mmap', 'addr perms path rss anon locked') - - @wrap_exceptions - def memory_maps(self): - def toaddr(start, end): - return "{}-{}".format( - hex(start)[2:].strip('L'), hex(end)[2:].strip('L') - ) - - procfs_path = self._procfs_path - retlist = [] - try: - rawlist = cext.proc_memory_maps(self.pid, procfs_path) - except OSError as err: - if err.errno == errno.EOVERFLOW and not IS_64_BIT: - # We may get here if we attempt to query a 64bit process - # with a 32bit python. - # Error originates from read() and also tools like "cat" - # fail in the same way (!). - # Since there simply is no way to determine CPU times we - # return 0.0 as a fallback. See: - # https://github.com/giampaolo/psutil/issues/857 - return [] - else: - raise - hit_enoent = False - for item in rawlist: - addr, addrsize, perm, name, rss, anon, locked = item - addr = toaddr(addr, addrsize) - if not name.startswith('['): - try: - name = os.readlink(f"{procfs_path}/{self.pid}/path/{name}") - except OSError as err: - if err.errno == errno.ENOENT: - # sometimes the link may not be resolved by - # readlink() even if it exists (ls shows it). - # If that's the case we just return the - # unresolved link path. - # This seems an inconsistency with /proc similar - # to: http://goo.gl/55XgO - name = f"{procfs_path}/{self.pid}/path/{name}" - hit_enoent = True - else: - raise - retlist.append((addr, perm, name, rss, anon, locked)) - if hit_enoent: - self._assert_alive() - return retlist - - @wrap_exceptions - def num_fds(self): - return len(os.listdir(f"{self._procfs_path}/{self.pid}/fd")) - - @wrap_exceptions - def num_ctx_switches(self): - return _common.pctxsw( - *cext.proc_num_ctx_switches(self.pid, self._procfs_path) - ) - - @wrap_exceptions - def wait(self, timeout=None): - return _psposix.wait_pid(self.pid, timeout, self._name) diff --git a/PortablePython/Lib/site-packages/psutil/_psutil_windows.pyd b/PortablePython/Lib/site-packages/psutil/_psutil_windows.pyd deleted file mode 100644 index 1e7f44e..0000000 Binary files a/PortablePython/Lib/site-packages/psutil/_psutil_windows.pyd and /dev/null differ diff --git a/PortablePython/Lib/site-packages/psutil/_pswindows.py b/PortablePython/Lib/site-packages/psutil/_pswindows.py deleted file mode 100644 index e5af3c9..0000000 --- a/PortablePython/Lib/site-packages/psutil/_pswindows.py +++ /dev/null @@ -1,1103 +0,0 @@ -# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -"""Windows platform implementation.""" - -import contextlib -import enum -import functools -import os -import signal -import sys -import time -from collections import namedtuple - -from . import _common -from ._common import ENCODING -from ._common import AccessDenied -from ._common import NoSuchProcess -from ._common import TimeoutExpired -from ._common import conn_tmap -from ._common import conn_to_ntuple -from ._common import debug -from ._common import isfile_strict -from ._common import memoize -from ._common import memoize_when_activated -from ._common import parse_environ_block -from ._common import usage_percent -from ._psutil_windows import ABOVE_NORMAL_PRIORITY_CLASS -from ._psutil_windows import BELOW_NORMAL_PRIORITY_CLASS -from ._psutil_windows import HIGH_PRIORITY_CLASS -from ._psutil_windows import IDLE_PRIORITY_CLASS -from ._psutil_windows import NORMAL_PRIORITY_CLASS -from ._psutil_windows import REALTIME_PRIORITY_CLASS - - -try: - from . import _psutil_windows as cext -except ImportError as err: - if ( - str(err).lower().startswith("dll load failed") - and sys.getwindowsversion()[0] < 6 - ): - # We may get here if: - # 1) we are on an old Windows version - # 2) psutil was installed via pip + wheel - # See: https://github.com/giampaolo/psutil/issues/811 - msg = "this Windows version is too old (< Windows Vista); " - msg += "psutil 3.4.2 is the latest version which supports Windows " - msg += "2000, XP and 2003 server" - raise RuntimeError(msg) from err - else: - raise - - -# process priority constants, import from __init__.py: -# http://msdn.microsoft.com/en-us/library/ms686219(v=vs.85).aspx -# fmt: off -__extra__all__ = [ - "win_service_iter", "win_service_get", - # Process priority - "ABOVE_NORMAL_PRIORITY_CLASS", "BELOW_NORMAL_PRIORITY_CLASS", - "HIGH_PRIORITY_CLASS", "IDLE_PRIORITY_CLASS", "NORMAL_PRIORITY_CLASS", - "REALTIME_PRIORITY_CLASS", - # IO priority - "IOPRIO_VERYLOW", "IOPRIO_LOW", "IOPRIO_NORMAL", "IOPRIO_HIGH", - # others - "CONN_DELETE_TCB", "AF_LINK", -] -# fmt: on - - -# ===================================================================== -# --- globals -# ===================================================================== - -CONN_DELETE_TCB = "DELETE_TCB" -ERROR_PARTIAL_COPY = 299 -PYPY = '__pypy__' in sys.builtin_module_names - -AddressFamily = enum.IntEnum('AddressFamily', {'AF_LINK': -1}) -AF_LINK = AddressFamily.AF_LINK - -TCP_STATUSES = { - cext.MIB_TCP_STATE_ESTAB: _common.CONN_ESTABLISHED, - cext.MIB_TCP_STATE_SYN_SENT: _common.CONN_SYN_SENT, - cext.MIB_TCP_STATE_SYN_RCVD: _common.CONN_SYN_RECV, - cext.MIB_TCP_STATE_FIN_WAIT1: _common.CONN_FIN_WAIT1, - cext.MIB_TCP_STATE_FIN_WAIT2: _common.CONN_FIN_WAIT2, - cext.MIB_TCP_STATE_TIME_WAIT: _common.CONN_TIME_WAIT, - cext.MIB_TCP_STATE_CLOSED: _common.CONN_CLOSE, - cext.MIB_TCP_STATE_CLOSE_WAIT: _common.CONN_CLOSE_WAIT, - cext.MIB_TCP_STATE_LAST_ACK: _common.CONN_LAST_ACK, - cext.MIB_TCP_STATE_LISTEN: _common.CONN_LISTEN, - cext.MIB_TCP_STATE_CLOSING: _common.CONN_CLOSING, - cext.MIB_TCP_STATE_DELETE_TCB: CONN_DELETE_TCB, - cext.PSUTIL_CONN_NONE: _common.CONN_NONE, -} - - -class Priority(enum.IntEnum): - ABOVE_NORMAL_PRIORITY_CLASS = ABOVE_NORMAL_PRIORITY_CLASS - BELOW_NORMAL_PRIORITY_CLASS = BELOW_NORMAL_PRIORITY_CLASS - HIGH_PRIORITY_CLASS = HIGH_PRIORITY_CLASS - IDLE_PRIORITY_CLASS = IDLE_PRIORITY_CLASS - NORMAL_PRIORITY_CLASS = NORMAL_PRIORITY_CLASS - REALTIME_PRIORITY_CLASS = REALTIME_PRIORITY_CLASS - - -globals().update(Priority.__members__) - - -class IOPriority(enum.IntEnum): - IOPRIO_VERYLOW = 0 - IOPRIO_LOW = 1 - IOPRIO_NORMAL = 2 - IOPRIO_HIGH = 3 - - -globals().update(IOPriority.__members__) - -pinfo_map = dict( - num_handles=0, - ctx_switches=1, - user_time=2, - kernel_time=3, - create_time=4, - num_threads=5, - io_rcount=6, - io_wcount=7, - io_rbytes=8, - io_wbytes=9, - io_count_others=10, - io_bytes_others=11, - num_page_faults=12, - peak_wset=13, - wset=14, - peak_paged_pool=15, - paged_pool=16, - peak_non_paged_pool=17, - non_paged_pool=18, - pagefile=19, - peak_pagefile=20, - mem_private=21, -) - - -# ===================================================================== -# --- named tuples -# ===================================================================== - - -# fmt: off -# psutil.cpu_times() -scputimes = namedtuple('scputimes', - ['user', 'system', 'idle', 'interrupt', 'dpc']) -# psutil.virtual_memory() -svmem = namedtuple('svmem', ['total', 'available', 'percent', 'used', 'free']) -# psutil.Process.memory_info() -pmem = namedtuple( - 'pmem', ['rss', 'vms', - 'num_page_faults', 'peak_wset', 'wset', 'peak_paged_pool', - 'paged_pool', 'peak_nonpaged_pool', 'nonpaged_pool', - 'pagefile', 'peak_pagefile', 'private']) -# psutil.Process.memory_full_info() -pfullmem = namedtuple('pfullmem', pmem._fields + ('uss', )) -# psutil.Process.memory_maps(grouped=True) -pmmap_grouped = namedtuple('pmmap_grouped', ['path', 'rss']) -# psutil.Process.memory_maps(grouped=False) -pmmap_ext = namedtuple( - 'pmmap_ext', 'addr perms ' + ' '.join(pmmap_grouped._fields)) -# psutil.Process.io_counters() -pio = namedtuple('pio', ['read_count', 'write_count', - 'read_bytes', 'write_bytes', - 'other_count', 'other_bytes']) -# fmt: on - - -# ===================================================================== -# --- utils -# ===================================================================== - - -@functools.lru_cache(maxsize=512) -def convert_dos_path(s): - r"""Convert paths using native DOS format like: - "\Device\HarddiskVolume1\Windows\systemew\file.txt" - into: - "C:\Windows\systemew\file.txt". - """ - rawdrive = '\\'.join(s.split('\\')[:3]) - driveletter = cext.QueryDosDevice(rawdrive) - remainder = s[len(rawdrive) :] - return os.path.join(driveletter, remainder) - - -@memoize -def getpagesize(): - return cext.getpagesize() - - -# ===================================================================== -# --- memory -# ===================================================================== - - -def virtual_memory(): - """System virtual memory as a namedtuple.""" - mem = cext.virtual_mem() - totphys, availphys, _totsys, _availsys = mem - total = totphys - avail = availphys - free = availphys - used = total - avail - percent = usage_percent((total - avail), total, round_=1) - return svmem(total, avail, percent, used, free) - - -def swap_memory(): - """Swap system memory as a (total, used, free, sin, sout) tuple.""" - mem = cext.virtual_mem() - - total_phys = mem[0] - total_system = mem[2] - - # system memory (commit total/limit) is the sum of physical and swap - # thus physical memory values need to be subtracted to get swap values - total = total_system - total_phys - # commit total is incremented immediately (decrementing free_system) - # while the corresponding free physical value is not decremented until - # pages are accessed, so we can't use free system memory for swap. - # instead, we calculate page file usage based on performance counter - if total > 0: - percentswap = cext.swap_percent() - used = int(0.01 * percentswap * total) - else: - percentswap = 0.0 - used = 0 - - free = total - used - percent = round(percentswap, 1) - return _common.sswap(total, used, free, percent, 0, 0) - - -# ===================================================================== -# --- disk -# ===================================================================== - - -disk_io_counters = cext.disk_io_counters - - -def disk_usage(path): - """Return disk usage associated with path.""" - if isinstance(path, bytes): - # XXX: do we want to use "strict"? Probably yes, in order - # to fail immediately. After all we are accepting input here... - path = path.decode(ENCODING, errors="strict") - total, free = cext.disk_usage(path) - used = total - free - percent = usage_percent(used, total, round_=1) - return _common.sdiskusage(total, used, free, percent) - - -def disk_partitions(all): - """Return disk partitions.""" - rawlist = cext.disk_partitions(all) - return [_common.sdiskpart(*x) for x in rawlist] - - -# ===================================================================== -# --- CPU -# ===================================================================== - - -def cpu_times(): - """Return system CPU times as a named tuple.""" - user, system, idle = cext.cpu_times() - # Internally, GetSystemTimes() is used, and it doesn't return - # interrupt and dpc times. cext.per_cpu_times() does, so we - # rely on it to get those only. - percpu_summed = scputimes(*[sum(n) for n in zip(*cext.per_cpu_times())]) - return scputimes( - user, system, idle, percpu_summed.interrupt, percpu_summed.dpc - ) - - -def per_cpu_times(): - """Return system per-CPU times as a list of named tuples.""" - ret = [] - for user, system, idle, interrupt, dpc in cext.per_cpu_times(): - item = scputimes(user, system, idle, interrupt, dpc) - ret.append(item) - return ret - - -def cpu_count_logical(): - """Return the number of logical CPUs in the system.""" - return cext.cpu_count_logical() - - -def cpu_count_cores(): - """Return the number of CPU cores in the system.""" - return cext.cpu_count_cores() - - -def cpu_stats(): - """Return CPU statistics.""" - ctx_switches, interrupts, _dpcs, syscalls = cext.cpu_stats() - soft_interrupts = 0 - return _common.scpustats( - ctx_switches, interrupts, soft_interrupts, syscalls - ) - - -def cpu_freq(): - """Return CPU frequency. - On Windows per-cpu frequency is not supported. - """ - curr, max_ = cext.cpu_freq() - min_ = 0.0 - return [_common.scpufreq(float(curr), min_, float(max_))] - - -_loadavg_inititialized = False - - -def getloadavg(): - """Return the number of processes in the system run queue averaged - over the last 1, 5, and 15 minutes respectively as a tuple. - """ - global _loadavg_inititialized - - if not _loadavg_inititialized: - cext.init_loadavg_counter() - _loadavg_inititialized = True - - # Drop to 2 decimal points which is what Linux does - raw_loads = cext.getloadavg() - return tuple(round(load, 2) for load in raw_loads) - - -# ===================================================================== -# --- network -# ===================================================================== - - -def net_connections(kind, _pid=-1): - """Return socket connections. If pid == -1 return system-wide - connections (as opposed to connections opened by one process only). - """ - families, types = conn_tmap[kind] - rawlist = cext.net_connections(_pid, families, types) - ret = set() - for item in rawlist: - fd, fam, type, laddr, raddr, status, pid = item - nt = conn_to_ntuple( - fd, - fam, - type, - laddr, - raddr, - status, - TCP_STATUSES, - pid=pid if _pid == -1 else None, - ) - ret.add(nt) - return list(ret) - - -def net_if_stats(): - """Get NIC stats (isup, duplex, speed, mtu).""" - ret = {} - rawdict = cext.net_if_stats() - for name, items in rawdict.items(): - isup, duplex, speed, mtu = items - if hasattr(_common, 'NicDuplex'): - duplex = _common.NicDuplex(duplex) - ret[name] = _common.snicstats(isup, duplex, speed, mtu, '') - return ret - - -def net_io_counters(): - """Return network I/O statistics for every network interface - installed on the system as a dict of raw tuples. - """ - return cext.net_io_counters() - - -def net_if_addrs(): - """Return the addresses associated to each NIC.""" - return cext.net_if_addrs() - - -# ===================================================================== -# --- sensors -# ===================================================================== - - -def sensors_battery(): - """Return battery information.""" - # For constants meaning see: - # https://msdn.microsoft.com/en-us/library/windows/desktop/ - # aa373232(v=vs.85).aspx - acline_status, flags, percent, secsleft = cext.sensors_battery() - power_plugged = acline_status == 1 - no_battery = bool(flags & 128) - charging = bool(flags & 8) - - if no_battery: - return None - if power_plugged or charging: - secsleft = _common.POWER_TIME_UNLIMITED - elif secsleft == -1: - secsleft = _common.POWER_TIME_UNKNOWN - - return _common.sbattery(percent, secsleft, power_plugged) - - -# ===================================================================== -# --- other system functions -# ===================================================================== - - -_last_btime = 0 - - -def boot_time(): - """The system boot time expressed in seconds since the epoch.""" - # This dirty hack is to adjust the precision of the returned - # value which may have a 1 second fluctuation, see: - # https://github.com/giampaolo/psutil/issues/1007 - global _last_btime - ret = float(cext.boot_time()) - if abs(ret - _last_btime) <= 1: - return _last_btime - else: - _last_btime = ret - return ret - - -def users(): - """Return currently connected users as a list of namedtuples.""" - retlist = [] - rawlist = cext.users() - for item in rawlist: - user, hostname, tstamp = item - nt = _common.suser(user, None, hostname, tstamp, None) - retlist.append(nt) - return retlist - - -# ===================================================================== -# --- Windows services -# ===================================================================== - - -def win_service_iter(): - """Yields a list of WindowsService instances.""" - for name, display_name in cext.winservice_enumerate(): - yield WindowsService(name, display_name) - - -def win_service_get(name): - """Open a Windows service and return it as a WindowsService instance.""" - service = WindowsService(name, None) - service._display_name = service._query_config()['display_name'] - return service - - -class WindowsService: # noqa: PLW1641 - """Represents an installed Windows service.""" - - def __init__(self, name, display_name): - self._name = name - self._display_name = display_name - - def __str__(self): - details = f"(name={self._name!r}, display_name={self._display_name!r})" - return f"{self.__class__.__name__}{details}" - - def __repr__(self): - return f"<{self.__str__()} at {id(self)}>" - - def __eq__(self, other): - # Test for equality with another WindosService object based - # on name. - if not isinstance(other, WindowsService): - return NotImplemented - return self._name == other._name - - def __ne__(self, other): - return not self == other - - def _query_config(self): - with self._wrap_exceptions(): - display_name, binpath, username, start_type = ( - cext.winservice_query_config(self._name) - ) - # XXX - update _self.display_name? - return dict( - display_name=display_name, - binpath=binpath, - username=username, - start_type=start_type, - ) - - def _query_status(self): - with self._wrap_exceptions(): - status, pid = cext.winservice_query_status(self._name) - if pid == 0: - pid = None - return dict(status=status, pid=pid) - - @contextlib.contextmanager - def _wrap_exceptions(self): - """Ctx manager which translates bare OSError and WindowsError - exceptions into NoSuchProcess and AccessDenied. - """ - try: - yield - except OSError as err: - name = self._name - if is_permission_err(err): - msg = ( - f"service {name!r} is not querable (not enough privileges)" - ) - raise AccessDenied(pid=None, name=name, msg=msg) from err - elif err.winerror in { - cext.ERROR_INVALID_NAME, - cext.ERROR_SERVICE_DOES_NOT_EXIST, - }: - msg = f"service {name!r} does not exist" - raise NoSuchProcess(pid=None, name=name, msg=msg) from err - else: - raise - - # config query - - def name(self): - """The service name. This string is how a service is referenced - and can be passed to win_service_get() to get a new - WindowsService instance. - """ - return self._name - - def display_name(self): - """The service display name. The value is cached when this class - is instantiated. - """ - return self._display_name - - def binpath(self): - """The fully qualified path to the service binary/exe file as - a string, including command line arguments. - """ - return self._query_config()['binpath'] - - def username(self): - """The name of the user that owns this service.""" - return self._query_config()['username'] - - def start_type(self): - """A string which can either be "automatic", "manual" or - "disabled". - """ - return self._query_config()['start_type'] - - # status query - - def pid(self): - """The process PID, if any, else None. This can be passed - to Process class to control the service's process. - """ - return self._query_status()['pid'] - - def status(self): - """Service status as a string.""" - return self._query_status()['status'] - - def description(self): - """Service long description.""" - return cext.winservice_query_descr(self.name()) - - # utils - - def as_dict(self): - """Utility method retrieving all the information above as a - dictionary. - """ - d = self._query_config() - d.update(self._query_status()) - d['name'] = self.name() - d['display_name'] = self.display_name() - d['description'] = self.description() - return d - - # actions - # XXX: the necessary C bindings for start() and stop() are - # implemented but for now I prefer not to expose them. - # I may change my mind in the future. Reasons: - # - they require Administrator privileges - # - can't implement a timeout for stop() (unless by using a thread, - # which sucks) - # - would require adding ServiceAlreadyStarted and - # ServiceAlreadyStopped exceptions, adding two new APIs. - # - we might also want to have modify(), which would basically mean - # rewriting win32serviceutil.ChangeServiceConfig, which involves a - # lot of stuff (and API constants which would pollute the API), see: - # http://pyxr.sourceforge.net/PyXR/c/python24/lib/site-packages/ - # win32/lib/win32serviceutil.py.html#0175 - # - psutil is typically about "read only" monitoring stuff; - # win_service_* APIs should only be used to retrieve a service and - # check whether it's running - - # def start(self, timeout=None): - # with self._wrap_exceptions(): - # cext.winservice_start(self.name()) - # if timeout: - # giveup_at = time.time() + timeout - # while True: - # if self.status() == "running": - # return - # else: - # if time.time() > giveup_at: - # raise TimeoutExpired(timeout) - # else: - # time.sleep(.1) - - # def stop(self): - # # Note: timeout is not implemented because it's just not - # # possible, see: - # # http://stackoverflow.com/questions/11973228/ - # with self._wrap_exceptions(): - # return cext.winservice_stop(self.name()) - - -# ===================================================================== -# --- processes -# ===================================================================== - - -pids = cext.pids -pid_exists = cext.pid_exists -ppid_map = cext.ppid_map # used internally by Process.children() - - -def is_permission_err(exc): - """Return True if this is a permission error.""" - assert isinstance(exc, OSError), exc - return isinstance(exc, PermissionError) or exc.winerror in { - cext.ERROR_ACCESS_DENIED, - cext.ERROR_PRIVILEGE_NOT_HELD, - } - - -def convert_oserror(exc, pid=None, name=None): - """Convert OSError into NoSuchProcess or AccessDenied.""" - assert isinstance(exc, OSError), exc - if is_permission_err(exc): - return AccessDenied(pid=pid, name=name) - if isinstance(exc, ProcessLookupError): - return NoSuchProcess(pid=pid, name=name) - raise exc - - -def wrap_exceptions(fun): - """Decorator which converts OSError into NoSuchProcess or AccessDenied.""" - - @functools.wraps(fun) - def wrapper(self, *args, **kwargs): - try: - return fun(self, *args, **kwargs) - except OSError as err: - raise convert_oserror(err, pid=self.pid, name=self._name) from err - - return wrapper - - -def retry_error_partial_copy(fun): - """Workaround for https://github.com/giampaolo/psutil/issues/875. - See: https://stackoverflow.com/questions/4457745#4457745. - """ - - @functools.wraps(fun) - def wrapper(self, *args, **kwargs): - delay = 0.0001 - times = 33 - for _ in range(times): # retries for roughly 1 second - try: - return fun(self, *args, **kwargs) - except OSError as _: - err = _ - if err.winerror == ERROR_PARTIAL_COPY: - time.sleep(delay) - delay = min(delay * 2, 0.04) - continue - raise - msg = ( - f"{fun} retried {times} times, converted to AccessDenied as it's " - f"still returning {err}" - ) - raise AccessDenied(pid=self.pid, name=self._name, msg=msg) - - return wrapper - - -class Process: - """Wrapper class around underlying C implementation.""" - - __slots__ = ["_cache", "_name", "_ppid", "pid"] - - def __init__(self, pid): - self.pid = pid - self._name = None - self._ppid = None - - # --- oneshot() stuff - - def oneshot_enter(self): - self._proc_info.cache_activate(self) - self.exe.cache_activate(self) - - def oneshot_exit(self): - self._proc_info.cache_deactivate(self) - self.exe.cache_deactivate(self) - - @memoize_when_activated - def _proc_info(self): - """Return multiple information about this process as a - raw tuple. - """ - ret = cext.proc_info(self.pid) - assert len(ret) == len(pinfo_map) - return ret - - def name(self): - """Return process name, which on Windows is always the final - part of the executable. - """ - # This is how PIDs 0 and 4 are always represented in taskmgr - # and process-hacker. - if self.pid == 0: - return "System Idle Process" - if self.pid == 4: - return "System" - return os.path.basename(self.exe()) - - @wrap_exceptions - @memoize_when_activated - def exe(self): - if PYPY: - try: - exe = cext.proc_exe(self.pid) - except OSError as err: - # 24 = ERROR_TOO_MANY_OPEN_FILES. Not sure why this happens - # (perhaps PyPy's JIT delaying garbage collection of files?). - if err.errno == 24: - debug(f"{err!r} translated into AccessDenied") - raise AccessDenied(self.pid, self._name) from err - raise - else: - exe = cext.proc_exe(self.pid) - if exe.startswith('\\'): - return convert_dos_path(exe) - return exe # May be "Registry", "MemCompression", ... - - @wrap_exceptions - @retry_error_partial_copy - def cmdline(self): - if cext.WINVER >= cext.WINDOWS_8_1: - # PEB method detects cmdline changes but requires more - # privileges: https://github.com/giampaolo/psutil/pull/1398 - try: - return cext.proc_cmdline(self.pid, use_peb=True) - except OSError as err: - if is_permission_err(err): - return cext.proc_cmdline(self.pid, use_peb=False) - else: - raise - else: - return cext.proc_cmdline(self.pid, use_peb=True) - - @wrap_exceptions - @retry_error_partial_copy - def environ(self): - s = cext.proc_environ(self.pid) - return parse_environ_block(s) - - def ppid(self): - try: - return ppid_map()[self.pid] - except KeyError: - raise NoSuchProcess(self.pid, self._name) from None - - def _get_raw_meminfo(self): - try: - return cext.proc_memory_info(self.pid) - except OSError as err: - if is_permission_err(err): - # TODO: the C ext can probably be refactored in order - # to get this from cext.proc_info() - debug("attempting memory_info() fallback (slower)") - info = self._proc_info() - return ( - info[pinfo_map['num_page_faults']], - info[pinfo_map['peak_wset']], - info[pinfo_map['wset']], - info[pinfo_map['peak_paged_pool']], - info[pinfo_map['paged_pool']], - info[pinfo_map['peak_non_paged_pool']], - info[pinfo_map['non_paged_pool']], - info[pinfo_map['pagefile']], - info[pinfo_map['peak_pagefile']], - info[pinfo_map['mem_private']], - ) - raise - - @wrap_exceptions - def memory_info(self): - # on Windows RSS == WorkingSetSize and VSM == PagefileUsage. - # Underlying C function returns fields of PROCESS_MEMORY_COUNTERS - # struct. - t = self._get_raw_meminfo() - rss = t[2] # wset - vms = t[7] # pagefile - return pmem(*(rss, vms) + t) - - @wrap_exceptions - def memory_full_info(self): - basic_mem = self.memory_info() - uss = cext.proc_memory_uss(self.pid) - uss *= getpagesize() - return pfullmem(*basic_mem + (uss,)) - - def memory_maps(self): - try: - raw = cext.proc_memory_maps(self.pid) - except OSError as err: - # XXX - can't use wrap_exceptions decorator as we're - # returning a generator; probably needs refactoring. - raise convert_oserror(err, self.pid, self._name) from err - else: - for addr, perm, path, rss in raw: - path = convert_dos_path(path) - addr = hex(addr) - yield (addr, perm, path, rss) - - @wrap_exceptions - def kill(self): - return cext.proc_kill(self.pid) - - @wrap_exceptions - def send_signal(self, sig): - if sig == signal.SIGTERM: - cext.proc_kill(self.pid) - elif sig in {signal.CTRL_C_EVENT, signal.CTRL_BREAK_EVENT}: - os.kill(self.pid, sig) - else: - msg = ( - "only SIGTERM, CTRL_C_EVENT and CTRL_BREAK_EVENT signals " - "are supported on Windows" - ) - raise ValueError(msg) - - @wrap_exceptions - def wait(self, timeout=None): - if timeout is None: - cext_timeout = cext.INFINITE - else: - # WaitForSingleObject() expects time in milliseconds. - cext_timeout = int(timeout * 1000) - - timer = getattr(time, 'monotonic', time.time) - stop_at = timer() + timeout if timeout is not None else None - - try: - # Exit code is supposed to come from GetExitCodeProcess(). - # May also be None if OpenProcess() failed with - # ERROR_INVALID_PARAMETER, meaning PID is already gone. - exit_code = cext.proc_wait(self.pid, cext_timeout) - except cext.TimeoutExpired as err: - # WaitForSingleObject() returned WAIT_TIMEOUT. Just raise. - raise TimeoutExpired(timeout, self.pid, self._name) from err - except cext.TimeoutAbandoned: - # WaitForSingleObject() returned WAIT_ABANDONED, see: - # https://github.com/giampaolo/psutil/issues/1224 - # We'll just rely on the internal polling and return None - # when the PID disappears. Subprocess module does the same - # (return None): - # https://github.com/python/cpython/blob/ - # be50a7b627d0aa37e08fa8e2d5568891f19903ce/ - # Lib/subprocess.py#L1193-L1194 - exit_code = None - - # At this point WaitForSingleObject() returned WAIT_OBJECT_0, - # meaning the process is gone. Stupidly there are cases where - # its PID may still stick around so we do a further internal - # polling. - delay = 0.0001 - while True: - if not pid_exists(self.pid): - return exit_code - if stop_at and timer() >= stop_at: - raise TimeoutExpired(timeout, pid=self.pid, name=self._name) - time.sleep(delay) - delay = min(delay * 2, 0.04) # incremental delay - - @wrap_exceptions - def username(self): - if self.pid in {0, 4}: - return 'NT AUTHORITY\\SYSTEM' - domain, user = cext.proc_username(self.pid) - return f"{domain}\\{user}" - - @wrap_exceptions - def create_time(self, fast_only=False): - # Note: proc_times() not put under oneshot() 'cause create_time() - # is already cached by the main Process class. - try: - _user, _system, created = cext.proc_times(self.pid) - return created - except OSError as err: - if is_permission_err(err): - if fast_only: - raise - debug("attempting create_time() fallback (slower)") - return self._proc_info()[pinfo_map['create_time']] - raise - - @wrap_exceptions - def num_threads(self): - return self._proc_info()[pinfo_map['num_threads']] - - @wrap_exceptions - def threads(self): - rawlist = cext.proc_threads(self.pid) - retlist = [] - for thread_id, utime, stime in rawlist: - ntuple = _common.pthread(thread_id, utime, stime) - retlist.append(ntuple) - return retlist - - @wrap_exceptions - def cpu_times(self): - try: - user, system, _created = cext.proc_times(self.pid) - except OSError as err: - if not is_permission_err(err): - raise - debug("attempting cpu_times() fallback (slower)") - info = self._proc_info() - user = info[pinfo_map['user_time']] - system = info[pinfo_map['kernel_time']] - # Children user/system times are not retrievable (set to 0). - return _common.pcputimes(user, system, 0.0, 0.0) - - @wrap_exceptions - def suspend(self): - cext.proc_suspend_or_resume(self.pid, True) - - @wrap_exceptions - def resume(self): - cext.proc_suspend_or_resume(self.pid, False) - - @wrap_exceptions - @retry_error_partial_copy - def cwd(self): - if self.pid in {0, 4}: - raise AccessDenied(self.pid, self._name) - # return a normalized pathname since the native C function appends - # "\\" at the and of the path - path = cext.proc_cwd(self.pid) - return os.path.normpath(path) - - @wrap_exceptions - def open_files(self): - if self.pid in {0, 4}: - return [] - ret = set() - # Filenames come in in native format like: - # "\Device\HarddiskVolume1\Windows\systemew\file.txt" - # Convert the first part in the corresponding drive letter - # (e.g. "C:\") by using Windows's QueryDosDevice() - raw_file_names = cext.proc_open_files(self.pid) - for file in raw_file_names: - file = convert_dos_path(file) - if isfile_strict(file): - ntuple = _common.popenfile(file, -1) - ret.add(ntuple) - return list(ret) - - @wrap_exceptions - def net_connections(self, kind='inet'): - return net_connections(kind, _pid=self.pid) - - @wrap_exceptions - def nice_get(self): - value = cext.proc_priority_get(self.pid) - value = Priority(value) - return value - - @wrap_exceptions - def nice_set(self, value): - return cext.proc_priority_set(self.pid, value) - - @wrap_exceptions - def ionice_get(self): - ret = cext.proc_io_priority_get(self.pid) - ret = IOPriority(ret) - return ret - - @wrap_exceptions - def ionice_set(self, ioclass, value): - if value: - msg = "value argument not accepted on Windows" - raise TypeError(msg) - if ioclass not in { - IOPriority.IOPRIO_VERYLOW, - IOPriority.IOPRIO_LOW, - IOPriority.IOPRIO_NORMAL, - IOPriority.IOPRIO_HIGH, - }: - msg = f"{ioclass} is not a valid priority" - raise ValueError(msg) - cext.proc_io_priority_set(self.pid, ioclass) - - @wrap_exceptions - def io_counters(self): - try: - ret = cext.proc_io_counters(self.pid) - except OSError as err: - if not is_permission_err(err): - raise - debug("attempting io_counters() fallback (slower)") - info = self._proc_info() - ret = ( - info[pinfo_map['io_rcount']], - info[pinfo_map['io_wcount']], - info[pinfo_map['io_rbytes']], - info[pinfo_map['io_wbytes']], - info[pinfo_map['io_count_others']], - info[pinfo_map['io_bytes_others']], - ) - return pio(*ret) - - @wrap_exceptions - def status(self): - suspended = cext.proc_is_suspended(self.pid) - if suspended: - return _common.STATUS_STOPPED - else: - return _common.STATUS_RUNNING - - @wrap_exceptions - def cpu_affinity_get(self): - def from_bitmask(x): - return [i for i in range(64) if (1 << i) & x] - - bitmask = cext.proc_cpu_affinity_get(self.pid) - return from_bitmask(bitmask) - - @wrap_exceptions - def cpu_affinity_set(self, value): - def to_bitmask(ls): - if not ls: - msg = f"invalid argument {ls!r}" - raise ValueError(msg) - out = 0 - for b in ls: - out |= 2**b - return out - - # SetProcessAffinityMask() states that ERROR_INVALID_PARAMETER - # is returned for an invalid CPU but this seems not to be true, - # therefore we check CPUs validy beforehand. - allcpus = list(range(len(per_cpu_times()))) - for cpu in value: - if cpu not in allcpus: - if not isinstance(cpu, int): - msg = f"invalid CPU {cpu!r}; an integer is required" - raise TypeError(msg) - msg = f"invalid CPU {cpu!r}" - raise ValueError(msg) - - bitmask = to_bitmask(value) - cext.proc_cpu_affinity_set(self.pid, bitmask) - - @wrap_exceptions - def num_handles(self): - try: - return cext.proc_num_handles(self.pid) - except OSError as err: - if is_permission_err(err): - debug("attempting num_handles() fallback (slower)") - return self._proc_info()[pinfo_map['num_handles']] - raise - - @wrap_exceptions - def num_ctx_switches(self): - ctx_switches = self._proc_info()[pinfo_map['ctx_switches']] - # only voluntary ctx switches are supported - return _common.pctxsw(ctx_switches, 0) diff --git a/PortablePython/Lib/site-packages/psutil/tests/__init__.py b/PortablePython/Lib/site-packages/psutil/tests/__init__.py deleted file mode 100644 index 5d4b3ab..0000000 --- a/PortablePython/Lib/site-packages/psutil/tests/__init__.py +++ /dev/null @@ -1,2025 +0,0 @@ -# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -"""Test utilities.""" - - -import atexit -import contextlib -import ctypes -import enum -import errno -import functools -import gc -import importlib -import ipaddress -import os -import platform -import random -import re -import select -import shlex -import shutil -import signal -import socket -import stat -import subprocess -import sys -import tempfile -import textwrap -import threading -import time -import unittest -import warnings -from socket import AF_INET -from socket import AF_INET6 -from socket import SOCK_STREAM - - -try: - import pytest -except ImportError: - pytest = None - -import psutil -from psutil import AIX -from psutil import LINUX -from psutil import MACOS -from psutil import NETBSD -from psutil import OPENBSD -from psutil import POSIX -from psutil import SUNOS -from psutil import WINDOWS -from psutil._common import bytes2human -from psutil._common import debug -from psutil._common import memoize -from psutil._common import print_color -from psutil._common import supports_ipv6 - - -if POSIX: - from psutil._psposix import wait_pid - - -# fmt: off -__all__ = [ - # constants - 'DEVNULL', 'GLOBAL_TIMEOUT', 'TOLERANCE_SYS_MEM', 'NO_RETRIES', - 'PYPY', 'PYTHON_EXE', 'PYTHON_EXE_ENV', 'ROOT_DIR', 'SCRIPTS_DIR', - 'TESTFN_PREFIX', 'UNICODE_SUFFIX', 'INVALID_UNICODE_SUFFIX', - 'CI_TESTING', 'VALID_PROC_STATUSES', 'TOLERANCE_DISK_USAGE', 'IS_64BIT', - "HAS_CPU_AFFINITY", "HAS_CPU_FREQ", "HAS_ENVIRON", "HAS_PROC_IO_COUNTERS", - "HAS_IONICE", "HAS_MEMORY_MAPS", "HAS_PROC_CPU_NUM", "HAS_RLIMIT", - "HAS_SENSORS_BATTERY", "HAS_BATTERY", "HAS_SENSORS_FANS", - "HAS_SENSORS_TEMPERATURES", "HAS_NET_CONNECTIONS_UNIX", "MACOS_11PLUS", - "MACOS_12PLUS", "COVERAGE", 'AARCH64', "PYTEST_PARALLEL", - # subprocesses - 'pyrun', 'terminate', 'reap_children', 'spawn_testproc', 'spawn_zombie', - 'spawn_children_pair', - # threads - 'ThreadTask', - # test utils - 'unittest', 'skip_on_access_denied', 'skip_on_not_implemented', - 'retry_on_failure', 'TestMemoryLeak', 'PsutilTestCase', - 'process_namespace', 'system_namespace', 'print_sysinfo', - 'is_win_secure_system_proc', 'fake_pytest', - # fs utils - 'chdir', 'safe_rmpath', 'create_py_exe', 'create_c_exe', 'get_testfn', - # os - 'get_winver', 'kernel_version', - # sync primitives - 'call_until', 'wait_for_pid', 'wait_for_file', - # network - 'check_net_address', 'filter_proc_net_connections', - 'get_free_port', 'bind_socket', 'bind_unix_socket', 'tcp_socketpair', - 'unix_socketpair', 'create_sockets', - # compat - 'reload_module', 'import_module_by_path', - # others - 'warn', 'copyload_shared_lib', 'is_namedtuple', -] -# fmt: on - - -# =================================================================== -# --- constants -# =================================================================== - -# --- platforms - -PYPY = '__pypy__' in sys.builtin_module_names -# whether we're running this test suite on a Continuous Integration service -GITHUB_ACTIONS = 'GITHUB_ACTIONS' in os.environ or 'CIBUILDWHEEL' in os.environ -CI_TESTING = GITHUB_ACTIONS -COVERAGE = 'COVERAGE_RUN' in os.environ -PYTEST_PARALLEL = "PYTEST_XDIST_WORKER" in os.environ # `make test-parallel` -# are we a 64 bit process? -IS_64BIT = sys.maxsize > 2**32 -AARCH64 = platform.machine() == "aarch64" - - -@memoize -def macos_version(): - version_str = platform.mac_ver()[0] - version = tuple(map(int, version_str.split(".")[:2])) - if version == (10, 16): - # When built against an older macOS SDK, Python will report - # macOS 10.16 instead of the real version. - version_str = subprocess.check_output( - [ - sys.executable, - "-sS", - "-c", - "import platform; print(platform.mac_ver()[0])", - ], - env={"SYSTEM_VERSION_COMPAT": "0"}, - universal_newlines=True, - ) - version = tuple(map(int, version_str.split(".")[:2])) - return version - - -if MACOS: - MACOS_11PLUS = macos_version() > (10, 15) - MACOS_12PLUS = macos_version() >= (12, 0) -else: - MACOS_11PLUS = False - MACOS_12PLUS = False - - -# --- configurable defaults - -# how many times retry_on_failure() decorator will retry -NO_RETRIES = 10 -# bytes tolerance for system-wide related tests -TOLERANCE_SYS_MEM = 5 * 1024 * 1024 # 5MB -TOLERANCE_DISK_USAGE = 10 * 1024 * 1024 # 10MB -# the timeout used in functions which have to wait -GLOBAL_TIMEOUT = 5 -# be more tolerant if we're on CI in order to avoid false positives -if CI_TESTING: - NO_RETRIES *= 3 - GLOBAL_TIMEOUT *= 3 - TOLERANCE_SYS_MEM *= 4 - TOLERANCE_DISK_USAGE *= 3 - -# --- file names - -# Disambiguate TESTFN for parallel testing. -if os.name == 'java': - # Jython disallows @ in module names - TESTFN_PREFIX = f"$psutil-{os.getpid()}-" -else: - TESTFN_PREFIX = f"@psutil-{os.getpid()}-" -UNICODE_SUFFIX = "-ƒőő" -# An invalid unicode string. -INVALID_UNICODE_SUFFIX = b"f\xc0\x80".decode('utf8', 'surrogateescape') -ASCII_FS = sys.getfilesystemencoding().lower() in {"ascii", "us-ascii"} - -# --- paths - -ROOT_DIR = os.path.realpath( - os.path.join(os.path.dirname(__file__), '..', '..') -) -SCRIPTS_DIR = os.environ.get( - "PSUTIL_SCRIPTS_DIR", os.path.join(ROOT_DIR, 'scripts') -) -HERE = os.path.realpath(os.path.dirname(__file__)) - -# --- support - -HAS_CPU_AFFINITY = hasattr(psutil.Process, "cpu_affinity") -HAS_CPU_FREQ = hasattr(psutil, "cpu_freq") -HAS_ENVIRON = hasattr(psutil.Process, "environ") -HAS_GETLOADAVG = hasattr(psutil, "getloadavg") -HAS_IONICE = hasattr(psutil.Process, "ionice") -HAS_MEMORY_MAPS = hasattr(psutil.Process, "memory_maps") -HAS_NET_CONNECTIONS_UNIX = POSIX and not SUNOS -HAS_NET_IO_COUNTERS = hasattr(psutil, "net_io_counters") -HAS_PROC_CPU_NUM = hasattr(psutil.Process, "cpu_num") -HAS_PROC_IO_COUNTERS = hasattr(psutil.Process, "io_counters") -HAS_RLIMIT = hasattr(psutil.Process, "rlimit") -HAS_SENSORS_BATTERY = hasattr(psutil, "sensors_battery") -try: - HAS_BATTERY = HAS_SENSORS_BATTERY and bool(psutil.sensors_battery()) -except Exception: # noqa: BLE001 - HAS_BATTERY = False -HAS_SENSORS_FANS = hasattr(psutil, "sensors_fans") -HAS_SENSORS_TEMPERATURES = hasattr(psutil, "sensors_temperatures") -HAS_THREADS = hasattr(psutil.Process, "threads") -SKIP_SYSCONS = (MACOS or AIX) and os.getuid() != 0 - -# --- misc - - -def _get_py_exe(): - def attempt(exe): - try: - subprocess.check_call( - [exe, "-V"], stdout=subprocess.PIPE, stderr=subprocess.PIPE - ) - except subprocess.CalledProcessError: - return None - else: - return exe - - env = os.environ.copy() - - # On Windows, starting with python 3.7, virtual environments use a - # venv launcher startup process. This does not play well when - # counting spawned processes, or when relying on the PID of the - # spawned process to do some checks, e.g. connections check per PID. - # Let's use the base python in this case. - base = getattr(sys, "_base_executable", None) - if WINDOWS and sys.version_info >= (3, 7) and base is not None: - # We need to set __PYVENV_LAUNCHER__ to sys.executable for the - # base python executable to know about the environment. - env["__PYVENV_LAUNCHER__"] = sys.executable - return base, env - elif GITHUB_ACTIONS: - return sys.executable, env - elif MACOS: - exe = ( - attempt(sys.executable) - or attempt(os.path.realpath(sys.executable)) - or attempt( - shutil.which("python{}.{}".format(*sys.version_info[:2])) - ) - or attempt(psutil.Process().exe()) - ) - if not exe: - raise ValueError("can't find python exe real abspath") - return exe, env - else: - exe = os.path.realpath(sys.executable) - assert os.path.exists(exe), exe - return exe, env - - -PYTHON_EXE, PYTHON_EXE_ENV = _get_py_exe() -DEVNULL = open(os.devnull, 'r+') # noqa: SIM115 -atexit.register(DEVNULL.close) - -VALID_PROC_STATUSES = [ - getattr(psutil, x) for x in dir(psutil) if x.startswith('STATUS_') -] -AF_UNIX = getattr(socket, "AF_UNIX", object()) - -_subprocesses_started = set() -_pids_started = set() - - -# =================================================================== -# --- threads -# =================================================================== - - -class ThreadTask(threading.Thread): - """A thread task which does nothing expect staying alive.""" - - def __init__(self): - super().__init__() - self._running = False - self._interval = 0.001 - self._flag = threading.Event() - - def __repr__(self): - name = self.__class__.__name__ - return f"<{name} running={self._running} at {id(self):#x}>" - - def __enter__(self): - self.start() - return self - - def __exit__(self, *args, **kwargs): - self.stop() - - def start(self): - """Start thread and keep it running until an explicit - stop() request. Polls for shutdown every 'timeout' seconds. - """ - if self._running: - raise ValueError("already started") - threading.Thread.start(self) - self._flag.wait() - - def run(self): - self._running = True - self._flag.set() - while self._running: - time.sleep(self._interval) - - def stop(self): - """Stop thread execution and and waits until it is stopped.""" - if not self._running: - raise ValueError("already stopped") - self._running = False - self.join() - - -# =================================================================== -# --- subprocesses -# =================================================================== - - -def _reap_children_on_err(fun): - @functools.wraps(fun) - def wrapper(*args, **kwargs): - try: - return fun(*args, **kwargs) - except Exception: - reap_children() - raise - - return wrapper - - -@_reap_children_on_err -def spawn_testproc(cmd=None, **kwds): - """Create a python subprocess which does nothing for some secs and - return it as a subprocess.Popen instance. - If "cmd" is specified that is used instead of python. - By default stdin and stdout are redirected to /dev/null. - It also attempts to make sure the process is in a reasonably - initialized state. - The process is registered for cleanup on reap_children(). - """ - kwds.setdefault("stdin", DEVNULL) - kwds.setdefault("stdout", DEVNULL) - kwds.setdefault("cwd", os.getcwd()) - kwds.setdefault("env", PYTHON_EXE_ENV) - if WINDOWS: - # Prevents the subprocess to open error dialogs. This will also - # cause stderr to be suppressed, which is suboptimal in order - # to debug broken tests. - CREATE_NO_WINDOW = 0x8000000 - kwds.setdefault("creationflags", CREATE_NO_WINDOW) - if cmd is None: - testfn = get_testfn(dir=os.getcwd()) - try: - safe_rmpath(testfn) - pyline = ( - "import time;" - f"open(r'{testfn}', 'w').close();" - "[time.sleep(0.1) for x in range(100)];" # 10 secs - ) - cmd = [PYTHON_EXE, "-c", pyline] - sproc = subprocess.Popen(cmd, **kwds) - _subprocesses_started.add(sproc) - wait_for_file(testfn, delete=True, empty=True) - finally: - safe_rmpath(testfn) - else: - sproc = subprocess.Popen(cmd, **kwds) - _subprocesses_started.add(sproc) - wait_for_pid(sproc.pid) - return sproc - - -@_reap_children_on_err -def spawn_children_pair(): - """Create a subprocess which creates another one as in: - A (us) -> B (child) -> C (grandchild). - Return a (child, grandchild) tuple. - The 2 processes are fully initialized and will live for 60 secs - and are registered for cleanup on reap_children(). - """ - tfile = None - testfn = get_testfn(dir=os.getcwd()) - try: - s = textwrap.dedent(f"""\ - import subprocess, os, sys, time - s = "import os, time;" - s += "f = open('{os.path.basename(testfn)}', 'w');" - s += "f.write(str(os.getpid()));" - s += "f.close();" - s += "[time.sleep(0.1) for x in range(100 * 6)];" - p = subprocess.Popen([r'{PYTHON_EXE}', '-c', s]) - p.wait() - """) - # On Windows if we create a subprocess with CREATE_NO_WINDOW flag - # set (which is the default) a "conhost.exe" extra process will be - # spawned as a child. We don't want that. - if WINDOWS: - subp, tfile = pyrun(s, creationflags=0) - else: - subp, tfile = pyrun(s) - child = psutil.Process(subp.pid) - grandchild_pid = int(wait_for_file(testfn, delete=True, empty=False)) - _pids_started.add(grandchild_pid) - grandchild = psutil.Process(grandchild_pid) - return (child, grandchild) - finally: - safe_rmpath(testfn) - if tfile is not None: - safe_rmpath(tfile) - - -def spawn_zombie(): - """Create a zombie process and return a (parent, zombie) process tuple. - In order to kill the zombie parent must be terminate()d first, then - zombie must be wait()ed on. - """ - assert psutil.POSIX - unix_file = get_testfn() - src = textwrap.dedent(f"""\ - import os, sys, time, socket, contextlib - child_pid = os.fork() - if child_pid > 0: - time.sleep(3000) - else: - # this is the zombie process - with socket.socket(socket.AF_UNIX) as s: - s.connect('{unix_file}') - pid = bytes(str(os.getpid()), 'ascii') - s.sendall(pid) - """) - tfile = None - sock = bind_unix_socket(unix_file) - try: - sock.settimeout(GLOBAL_TIMEOUT) - parent, tfile = pyrun(src) - conn, _ = sock.accept() - try: - select.select([conn.fileno()], [], [], GLOBAL_TIMEOUT) - zpid = int(conn.recv(1024)) - _pids_started.add(zpid) - zombie = psutil.Process(zpid) - call_until(lambda: zombie.status() == psutil.STATUS_ZOMBIE) - return (parent, zombie) - finally: - conn.close() - finally: - sock.close() - safe_rmpath(unix_file) - if tfile is not None: - safe_rmpath(tfile) - - -@_reap_children_on_err -def pyrun(src, **kwds): - """Run python 'src' code string in a separate interpreter. - Returns a subprocess.Popen instance and the test file where the source - code was written. - """ - kwds.setdefault("stdout", None) - kwds.setdefault("stderr", None) - srcfile = get_testfn() - try: - with open(srcfile, "w") as f: - f.write(src) - subp = spawn_testproc([PYTHON_EXE, f.name], **kwds) - wait_for_pid(subp.pid) - return (subp, srcfile) - except Exception: - safe_rmpath(srcfile) - raise - - -@_reap_children_on_err -def sh(cmd, **kwds): - """Run cmd in a subprocess and return its output. - raises RuntimeError on error. - """ - # Prevents subprocess to open error dialogs in case of error. - flags = 0x8000000 if WINDOWS else 0 - kwds.setdefault("stdout", subprocess.PIPE) - kwds.setdefault("stderr", subprocess.PIPE) - kwds.setdefault("universal_newlines", True) - kwds.setdefault("creationflags", flags) - if isinstance(cmd, str): - cmd = shlex.split(cmd) - p = subprocess.Popen(cmd, **kwds) - _subprocesses_started.add(p) - stdout, stderr = p.communicate(timeout=GLOBAL_TIMEOUT) - if p.returncode != 0: - raise RuntimeError(stdout + stderr) - if stderr: - warn(stderr) - if stdout.endswith('\n'): - stdout = stdout[:-1] - return stdout - - -def terminate(proc_or_pid, sig=signal.SIGTERM, wait_timeout=GLOBAL_TIMEOUT): - """Terminate a process and wait() for it. - Process can be a PID or an instance of psutil.Process(), - subprocess.Popen() or psutil.Popen(). - If it's a subprocess.Popen() or psutil.Popen() instance also closes - its stdin / stdout / stderr fds. - PID is wait()ed even if the process is already gone (kills zombies). - Does nothing if the process does not exist. - Return process exit status. - """ - - def wait(proc, timeout): - proc.wait(timeout) - if WINDOWS and isinstance(proc, subprocess.Popen): - # Otherwise PID may still hang around. - try: - return psutil.Process(proc.pid).wait(timeout) - except psutil.NoSuchProcess: - pass - - def sendsig(proc, sig): - # XXX: otherwise the build hangs for some reason. - if MACOS and GITHUB_ACTIONS: - sig = signal.SIGKILL - # If the process received SIGSTOP, SIGCONT is necessary first, - # otherwise SIGTERM won't work. - if POSIX and sig != signal.SIGKILL: - proc.send_signal(signal.SIGCONT) - proc.send_signal(sig) - - def term_subprocess_proc(proc, timeout): - try: - sendsig(proc, sig) - except ProcessLookupError: - pass - except OSError as err: - if WINDOWS and err.winerror == 6: # "invalid handle" - pass - raise - return wait(proc, timeout) - - def term_psutil_proc(proc, timeout): - try: - sendsig(proc, sig) - except psutil.NoSuchProcess: - pass - return wait(proc, timeout) - - def term_pid(pid, timeout): - try: - proc = psutil.Process(pid) - except psutil.NoSuchProcess: - # Needed to kill zombies. - if POSIX: - return wait_pid(pid, timeout) - else: - return term_psutil_proc(proc, timeout) - - def flush_popen(proc): - if proc.stdout: - proc.stdout.close() - if proc.stderr: - proc.stderr.close() - # Flushing a BufferedWriter may raise an error. - if proc.stdin: - proc.stdin.close() - - p = proc_or_pid - try: - if isinstance(p, int): - return term_pid(p, wait_timeout) - elif isinstance(p, (psutil.Process, psutil.Popen)): - return term_psutil_proc(p, wait_timeout) - elif isinstance(p, subprocess.Popen): - return term_subprocess_proc(p, wait_timeout) - else: - raise TypeError(f"wrong type {p!r}") - finally: - if isinstance(p, (subprocess.Popen, psutil.Popen)): - flush_popen(p) - pid = p if isinstance(p, int) else p.pid - assert not psutil.pid_exists(pid), pid - - -def reap_children(recursive=False): - """Terminate and wait() any subprocess started by this test suite - and any children currently running, ensuring that no processes stick - around to hog resources. - If recursive is True it also tries to terminate and wait() - all grandchildren started by this process. - """ - # Get the children here before terminating them, as in case of - # recursive=True we don't want to lose the intermediate reference - # pointing to the grandchildren. - children = psutil.Process().children(recursive=recursive) - - # Terminate subprocess.Popen. - while _subprocesses_started: - subp = _subprocesses_started.pop() - terminate(subp) - - # Collect started pids. - while _pids_started: - pid = _pids_started.pop() - terminate(pid) - - # Terminate children. - if children: - for p in children: - terminate(p, wait_timeout=None) - _, alive = psutil.wait_procs(children, timeout=GLOBAL_TIMEOUT) - for p in alive: - warn(f"couldn't terminate process {p!r}; attempting kill()") - terminate(p, sig=signal.SIGKILL) - - -# =================================================================== -# --- OS -# =================================================================== - - -def kernel_version(): - """Return a tuple such as (2, 6, 36).""" - if not POSIX: - raise NotImplementedError("not POSIX") - s = "" - uname = os.uname()[2] - for c in uname: - if c.isdigit() or c == '.': - s += c - else: - break - if not s: - raise ValueError(f"can't parse {uname!r}") - minor = 0 - micro = 0 - nums = s.split('.') - major = int(nums[0]) - if len(nums) >= 2: - minor = int(nums[1]) - if len(nums) >= 3: - micro = int(nums[2]) - return (major, minor, micro) - - -def get_winver(): - if not WINDOWS: - raise NotImplementedError("not WINDOWS") - wv = sys.getwindowsversion() - sp = wv.service_pack_major or 0 - return (wv[0], wv[1], sp) - - -# =================================================================== -# --- sync primitives -# =================================================================== - - -class retry: - """A retry decorator.""" - - def __init__( - self, - exception=Exception, - timeout=None, - retries=None, - interval=0.001, - logfun=None, - ): - if timeout and retries: - raise ValueError("timeout and retries args are mutually exclusive") - self.exception = exception - self.timeout = timeout - self.retries = retries - self.interval = interval - self.logfun = logfun - - def __iter__(self): - if self.timeout: - stop_at = time.time() + self.timeout - while time.time() < stop_at: - yield - elif self.retries: - for _ in range(self.retries): - yield - else: - while True: - yield - - def sleep(self): - if self.interval is not None: - time.sleep(self.interval) - - def __call__(self, fun): - @functools.wraps(fun) - def wrapper(*args, **kwargs): - exc = None - for _ in self: - try: - return fun(*args, **kwargs) - except self.exception as _: - exc = _ - if self.logfun is not None: - self.logfun(exc) - self.sleep() - continue - - raise exc - - # This way the user of the decorated function can change config - # parameters. - wrapper.decorator = self - return wrapper - - -@retry( - exception=psutil.NoSuchProcess, - logfun=None, - timeout=GLOBAL_TIMEOUT, - interval=0.001, -) -def wait_for_pid(pid): - """Wait for pid to show up in the process list then return. - Used in the test suite to give time the sub process to initialize. - """ - if pid not in psutil.pids(): - raise psutil.NoSuchProcess(pid) - psutil.Process(pid) - - -@retry( - exception=(FileNotFoundError, AssertionError), - logfun=None, - timeout=GLOBAL_TIMEOUT, - interval=0.001, -) -def wait_for_file(fname, delete=True, empty=False): - """Wait for a file to be written on disk with some content.""" - with open(fname, "rb") as f: - data = f.read() - if not empty: - assert data - if delete: - safe_rmpath(fname) - return data - - -@retry( - exception=AssertionError, - logfun=None, - timeout=GLOBAL_TIMEOUT, - interval=0.001, -) -def call_until(fun): - """Keep calling function until it evaluates to True.""" - ret = fun() - assert ret - return ret - - -# =================================================================== -# --- fs -# =================================================================== - - -def safe_rmpath(path): - """Convenience function for removing temporary test files or dirs.""" - - def retry_fun(fun): - # On Windows it could happen that the file or directory has - # open handles or references preventing the delete operation - # to succeed immediately, so we retry for a while. See: - # https://bugs.python.org/issue33240 - stop_at = time.time() + GLOBAL_TIMEOUT - while time.time() < stop_at: - try: - return fun() - except FileNotFoundError: - pass - except OSError as _: - err = _ - warn(f"ignoring {err}") - time.sleep(0.01) - raise err - - try: - st = os.stat(path) - if stat.S_ISDIR(st.st_mode): - fun = functools.partial(shutil.rmtree, path) - else: - fun = functools.partial(os.remove, path) - if POSIX: - fun() - else: - retry_fun(fun) - except FileNotFoundError: - pass - - -def safe_mkdir(dir): - """Convenience function for creating a directory.""" - try: - os.mkdir(dir) - except FileExistsError: - pass - - -@contextlib.contextmanager -def chdir(dirname): - """Context manager which temporarily changes the current directory.""" - curdir = os.getcwd() - try: - os.chdir(dirname) - yield - finally: - os.chdir(curdir) - - -def create_py_exe(path): - """Create a Python executable file in the given location.""" - assert not os.path.exists(path), path - atexit.register(safe_rmpath, path) - shutil.copyfile(PYTHON_EXE, path) - if POSIX: - st = os.stat(path) - os.chmod(path, st.st_mode | stat.S_IEXEC) - return path - - -def create_c_exe(path, c_code=None): - """Create a compiled C executable in the given location.""" - assert not os.path.exists(path), path - if not shutil.which("gcc"): - raise pytest.skip("gcc is not installed") - if c_code is None: - c_code = textwrap.dedent(""" - #include - int main() { - pause(); - return 1; - } - """) - else: - assert isinstance(c_code, str), c_code - - atexit.register(safe_rmpath, path) - with open(get_testfn(suffix='.c'), "w") as f: - f.write(c_code) - try: - subprocess.check_call(["gcc", f.name, "-o", path]) - finally: - safe_rmpath(f.name) - return path - - -def get_testfn(suffix="", dir=None): - """Return an absolute pathname of a file or dir that did not - exist at the time this call is made. Also schedule it for safe - deletion at interpreter exit. It's technically racy but probably - not really due to the time variant. - """ - while True: - name = tempfile.mktemp(prefix=TESTFN_PREFIX, suffix=suffix, dir=dir) - if not os.path.exists(name): # also include dirs - path = os.path.realpath(name) # needed for OSX - atexit.register(safe_rmpath, path) - return path - - -# =================================================================== -# --- testing -# =================================================================== - - -class fake_pytest: - """A class that mimics some basic pytest APIs. This is meant for - when unit tests are run in production, where pytest may not be - installed. Still, the user can test psutil installation via: - - $ python3 -m psutil.tests - """ - - @staticmethod - def main(*args, **kw): # noqa: ARG004 - """Mimics pytest.main(). It has the same effect as running - `python3 -m unittest -v` from the project root directory. - """ - suite = unittest.TestLoader().discover(HERE) - unittest.TextTestRunner(verbosity=2).run(suite) - warnings.warn( - "Fake pytest module was used. Test results may be inaccurate.", - UserWarning, - stacklevel=1, - ) - return suite - - @staticmethod - def raises(exc, match=None): - """Mimics `pytest.raises`.""" - - class ExceptionInfo: - _exc = None - - @property - def value(self): - return self._exc - - @contextlib.contextmanager - def context(exc, match=None): - einfo = ExceptionInfo() - try: - yield einfo - except exc as err: - if match and not re.search(match, str(err)): - msg = f'"{match}" does not match "{err}"' - raise AssertionError(msg) - einfo._exc = err - else: - raise AssertionError(f"{exc!r} not raised") - - return context(exc, match=match) - - @staticmethod - def warns(warning, match=None): - """Mimics `pytest.warns`.""" - if match: - return unittest.TestCase().assertWarnsRegex(warning, match) - return unittest.TestCase().assertWarns(warning) - - @staticmethod - def skip(reason=""): - """Mimics `unittest.SkipTest`.""" - raise unittest.SkipTest(reason) - - class mark: - - @staticmethod - def skipif(condition, reason=""): - """Mimics `@pytest.mark.skipif` decorator.""" - return unittest.skipIf(condition, reason) - - class xdist_group: - """Mimics `@pytest.mark.xdist_group` decorator (no-op).""" - - def __init__(self, name=None): - pass - - def __call__(self, cls_or_meth): - return cls_or_meth - - -if pytest is None: - pytest = fake_pytest - - -class PsutilTestCase(unittest.TestCase): - """Test class providing auto-cleanup wrappers on top of process - test utilities. All test classes should derive from this one, even - if we use pytest. - """ - - def get_testfn(self, suffix="", dir=None): - fname = get_testfn(suffix=suffix, dir=dir) - self.addCleanup(safe_rmpath, fname) - return fname - - def spawn_testproc(self, *args, **kwds): - sproc = spawn_testproc(*args, **kwds) - self.addCleanup(terminate, sproc) - return sproc - - def spawn_children_pair(self): - child1, child2 = spawn_children_pair() - self.addCleanup(terminate, child2) - self.addCleanup(terminate, child1) # executed first - return (child1, child2) - - def spawn_zombie(self): - parent, zombie = spawn_zombie() - self.addCleanup(terminate, zombie) - self.addCleanup(terminate, parent) # executed first - return (parent, zombie) - - def pyrun(self, *args, **kwds): - sproc, srcfile = pyrun(*args, **kwds) - self.addCleanup(safe_rmpath, srcfile) - self.addCleanup(terminate, sproc) # executed first - return sproc - - def _check_proc_exc(self, proc, exc): - assert isinstance(exc, psutil.Error) - assert exc.pid == proc.pid - assert exc.name == proc._name - if exc.name: - assert exc.name - if isinstance(exc, psutil.ZombieProcess): - assert exc.ppid == proc._ppid - if exc.ppid is not None: - assert exc.ppid >= 0 - str(exc) - repr(exc) - - def assertPidGone(self, pid): - with pytest.raises(psutil.NoSuchProcess) as cm: - try: - psutil.Process(pid) - except psutil.ZombieProcess: - raise AssertionError("wasn't supposed to raise ZombieProcess") - assert cm.value.pid == pid - assert cm.value.name is None - assert not psutil.pid_exists(pid), pid - assert pid not in psutil.pids() - assert pid not in [x.pid for x in psutil.process_iter()] - - def assertProcessGone(self, proc): - self.assertPidGone(proc.pid) - ns = process_namespace(proc) - for fun, name in ns.iter(ns.all, clear_cache=True): - with self.subTest(proc=proc, name=name): - try: - ret = fun() - except psutil.ZombieProcess: - raise - except psutil.NoSuchProcess as exc: - self._check_proc_exc(proc, exc) - else: - msg = ( - f"Process.{name}() didn't raise NSP and returned" - f" {ret!r}" - ) - raise AssertionError(msg) - proc.wait(timeout=0) # assert not raise TimeoutExpired - - def assertProcessZombie(self, proc): - # A zombie process should always be instantiable. - clone = psutil.Process(proc.pid) - # Cloned zombie on Open/NetBSD has null creation time, see: - # https://github.com/giampaolo/psutil/issues/2287 - assert proc == clone - if not (OPENBSD or NETBSD): - assert hash(proc) == hash(clone) - # Its status always be querable. - assert proc.status() == psutil.STATUS_ZOMBIE - # It should be considered 'running'. - assert proc.is_running() - assert psutil.pid_exists(proc.pid) - # as_dict() shouldn't crash. - proc.as_dict() - # It should show up in pids() and process_iter(). - assert proc.pid in psutil.pids() - assert proc.pid in [x.pid for x in psutil.process_iter()] - psutil._pmap = {} - assert proc.pid in [x.pid for x in psutil.process_iter()] - # Call all methods. - ns = process_namespace(proc) - for fun, name in ns.iter(ns.all, clear_cache=True): - with self.subTest(proc=proc, name=name): - try: - fun() - except (psutil.ZombieProcess, psutil.AccessDenied) as exc: - self._check_proc_exc(proc, exc) - if LINUX: - # https://github.com/giampaolo/psutil/pull/2288 - with pytest.raises(psutil.ZombieProcess) as cm: - proc.cmdline() - self._check_proc_exc(proc, cm.value) - with pytest.raises(psutil.ZombieProcess) as cm: - proc.exe() - self._check_proc_exc(proc, cm.value) - with pytest.raises(psutil.ZombieProcess) as cm: - proc.memory_maps() - self._check_proc_exc(proc, cm.value) - # Zombie cannot be signaled or terminated. - proc.suspend() - proc.resume() - proc.terminate() - proc.kill() - assert proc.is_running() - assert psutil.pid_exists(proc.pid) - assert proc.pid in psutil.pids() - assert proc.pid in [x.pid for x in psutil.process_iter()] - psutil._pmap = {} - assert proc.pid in [x.pid for x in psutil.process_iter()] - - # Its parent should 'see' it (edit: not true on BSD and MACOS). - # descendants = [x.pid for x in psutil.Process().children( - # recursive=True)] - # self.assertIn(proc.pid, descendants) - - # __eq__ can't be relied upon because creation time may not be - # querable. - # self.assertEqual(proc, psutil.Process(proc.pid)) - - # XXX should we also assume ppid() to be usable? Note: this - # would be an important use case as the only way to get - # rid of a zombie is to kill its parent. - # self.assertEqual(proc.ppid(), os.getpid()) - - -@pytest.mark.skipif(PYPY, reason="unreliable on PYPY") -class TestMemoryLeak(PsutilTestCase): - """Test framework class for detecting function memory leaks, - typically functions implemented in C which forgot to free() memory - from the heap. It does so by checking whether the process memory - usage increased before and after calling the function many times. - - Note that this is hard (probably impossible) to do reliably, due - to how the OS handles memory, the GC and so on (memory can even - decrease!). In order to avoid false positives, in case of failure - (mem > 0) we retry the test for up to 5 times, increasing call - repetitions each time. If the memory keeps increasing then it's a - failure. - - If available (Linux, OSX, Windows), USS memory is used for comparison, - since it's supposed to be more precise, see: - https://gmpy.dev/blog/2016/real-process-memory-and-environ-in-python - If not, RSS memory is used. mallinfo() on Linux and _heapwalk() on - Windows may give even more precision, but at the moment are not - implemented. - - PyPy appears to be completely unstable for this framework, probably - because of its JIT, so tests on PYPY are skipped. - - Usage: - - class TestLeaks(psutil.tests.TestMemoryLeak): - - def test_fun(self): - self.execute(some_function) - """ - - # Configurable class attrs. - times = 200 - warmup_times = 10 - tolerance = 0 # memory - retries = 10 if CI_TESTING else 5 - verbose = True - _thisproc = psutil.Process() - _psutil_debug_orig = bool(os.getenv('PSUTIL_DEBUG')) - - @classmethod - def setUpClass(cls): - psutil._set_debug(False) # avoid spamming to stderr - - @classmethod - def tearDownClass(cls): - psutil._set_debug(cls._psutil_debug_orig) - - def _get_mem(self): - # USS is the closest thing we have to "real" memory usage and it - # should be less likely to produce false positives. - mem = self._thisproc.memory_full_info() - return getattr(mem, "uss", mem.rss) - - def _get_num_fds(self): - if POSIX: - return self._thisproc.num_fds() - else: - return self._thisproc.num_handles() - - def _log(self, msg): - if self.verbose: - print_color(msg, color="yellow", file=sys.stderr) - - def _check_fds(self, fun): - """Makes sure num_fds() (POSIX) or num_handles() (Windows) does - not increase after calling a function. Used to discover forgotten - close(2) and CloseHandle syscalls. - """ - before = self._get_num_fds() - self.call(fun) - after = self._get_num_fds() - diff = after - before - if diff < 0: - msg = ( - f"negative diff {diff!r} (gc probably collected a" - " resource from a previous test)" - ) - raise self.fail(msg) - if diff > 0: - type_ = "fd" if POSIX else "handle" - if diff > 1: - type_ += "s" - msg = f"{diff} unclosed {type_} after calling {fun!r}" - raise self.fail(msg) - - def _call_ntimes(self, fun, times): - """Get 2 distinct memory samples, before and after having - called fun repeatedly, and return the memory difference. - """ - gc.collect(generation=1) - mem1 = self._get_mem() - for x in range(times): - ret = self.call(fun) - del x, ret - gc.collect(generation=1) - mem2 = self._get_mem() - assert gc.garbage == [] - diff = mem2 - mem1 # can also be negative - return diff - - def _check_mem(self, fun, times, retries, tolerance): - messages = [] - prev_mem = 0 - increase = times - for idx in range(1, retries + 1): - mem = self._call_ntimes(fun, times) - msg = "Run #{}: extra-mem={}, per-call={}, calls={}".format( - idx, - bytes2human(mem), - bytes2human(mem / times), - times, - ) - messages.append(msg) - success = mem <= tolerance or mem <= prev_mem - if success: - if idx > 1: - self._log(msg) - return - else: - if idx == 1: - print() # noqa: T201 - self._log(msg) - times += increase - prev_mem = mem - raise self.fail(". ".join(messages)) - - # --- - - def call(self, fun): - return fun() - - def execute( - self, fun, times=None, warmup_times=None, retries=None, tolerance=None - ): - """Test a callable.""" - times = times if times is not None else self.times - warmup_times = ( - warmup_times if warmup_times is not None else self.warmup_times - ) - retries = retries if retries is not None else self.retries - tolerance = tolerance if tolerance is not None else self.tolerance - try: - assert times >= 1, "times must be >= 1" - assert warmup_times >= 0, "warmup_times must be >= 0" - assert retries >= 0, "retries must be >= 0" - assert tolerance >= 0, "tolerance must be >= 0" - except AssertionError as err: - raise ValueError(str(err)) - - self._call_ntimes(fun, warmup_times) # warm up - self._check_fds(fun) - self._check_mem(fun, times=times, retries=retries, tolerance=tolerance) - - def execute_w_exc(self, exc, fun, **kwargs): - """Convenience method to test a callable while making sure it - raises an exception on every call. - """ - - def call(): - self.assertRaises(exc, fun) - - self.execute(call, **kwargs) - - -def print_sysinfo(): - import collections - import datetime - import getpass - import locale - import pprint - - try: - import pip - except ImportError: - pip = None - try: - import wheel - except ImportError: - wheel = None - - info = collections.OrderedDict() - - # OS - if psutil.LINUX and shutil.which("lsb_release"): - info['OS'] = sh('lsb_release -d -s') - elif psutil.OSX: - info['OS'] = f"Darwin {platform.mac_ver()[0]}" - elif psutil.WINDOWS: - info['OS'] = "Windows " + ' '.join(map(str, platform.win32_ver())) - if hasattr(platform, 'win32_edition'): - info['OS'] += ", " + platform.win32_edition() - else: - info['OS'] = f"{platform.system()} {platform.version()}" - info['arch'] = ', '.join( - list(platform.architecture()) + [platform.machine()] - ) - if psutil.POSIX: - info['kernel'] = platform.uname()[2] - - # python - info['python'] = ', '.join([ - platform.python_implementation(), - platform.python_version(), - platform.python_compiler(), - ]) - info['pip'] = getattr(pip, '__version__', 'not installed') - if wheel is not None: - info['pip'] += f" (wheel={wheel.__version__})" - - # UNIX - if psutil.POSIX: - if shutil.which("gcc"): - out = sh(['gcc', '--version']) - info['gcc'] = str(out).split('\n')[0] - else: - info['gcc'] = 'not installed' - s = platform.libc_ver()[1] - if s: - info['glibc'] = s - - # system - info['fs-encoding'] = sys.getfilesystemencoding() - lang = locale.getlocale() - info['lang'] = f"{lang[0]}, {lang[1]}" - info['boot-time'] = datetime.datetime.fromtimestamp( - psutil.boot_time() - ).strftime("%Y-%m-%d %H:%M:%S") - info['time'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") - info['user'] = getpass.getuser() - info['home'] = os.path.expanduser("~") - info['cwd'] = os.getcwd() - info['pyexe'] = PYTHON_EXE - info['hostname'] = platform.node() - info['PID'] = os.getpid() - - # metrics - info['cpus'] = psutil.cpu_count() - info['loadavg'] = "{:.1f}%, {:.1f}%, {:.1f}%".format( - *tuple(x / psutil.cpu_count() * 100 for x in psutil.getloadavg()) - ) - mem = psutil.virtual_memory() - info['memory'] = "{}%%, used={}, total={}".format( - int(mem.percent), - bytes2human(mem.used), - bytes2human(mem.total), - ) - swap = psutil.swap_memory() - info['swap'] = "{}%%, used={}, total={}".format( - int(swap.percent), - bytes2human(swap.used), - bytes2human(swap.total), - ) - info['pids'] = len(psutil.pids()) - pinfo = psutil.Process().as_dict() - pinfo.pop('memory_maps', None) - info['proc'] = pprint.pformat(pinfo) - - print("=" * 70, file=sys.stderr) # noqa: T201 - for k, v in info.items(): - print("{:<17} {}".format(k + ":", v), file=sys.stderr) # noqa: T201 - print("=" * 70, file=sys.stderr) # noqa: T201 - sys.stdout.flush() - - # if WINDOWS: - # os.system("tasklist") - # elif shutil.which("ps"): - # os.system("ps aux") - # print("=" * 70, file=sys.stderr) - - sys.stdout.flush() - - -def is_win_secure_system_proc(pid): - # see: https://github.com/giampaolo/psutil/issues/2338 - @memoize - def get_procs(): - ret = {} - out = sh("tasklist.exe /NH /FO csv") - for line in out.splitlines()[1:]: - bits = [x.replace('"', "") for x in line.split(",")] - name, pid = bits[0], int(bits[1]) - ret[pid] = name - return ret - - try: - return get_procs()[pid] == "Secure System" - except KeyError: - return False - - -def _get_eligible_cpu(): - p = psutil.Process() - if hasattr(p, "cpu_num"): - return p.cpu_num() - elif hasattr(p, "cpu_affinity"): - return random.choice(p.cpu_affinity()) - return 0 - - -class process_namespace: - """A container that lists all Process class method names + some - reasonable parameters to be called with. Utility methods (parent(), - children(), ...) are excluded. - - >>> ns = process_namespace(psutil.Process()) - >>> for fun, name in ns.iter(ns.getters): - ... fun() - """ - - utils = [('cpu_percent', (), {}), ('memory_percent', (), {})] - - ignored = [ - ('as_dict', (), {}), - ('children', (), {'recursive': True}), - ('connections', (), {}), # deprecated - ('is_running', (), {}), - ('oneshot', (), {}), - ('parent', (), {}), - ('parents', (), {}), - ('pid', (), {}), - ('wait', (0,), {}), - ] - - getters = [ - ('cmdline', (), {}), - ('cpu_times', (), {}), - ('create_time', (), {}), - ('cwd', (), {}), - ('exe', (), {}), - ('memory_full_info', (), {}), - ('memory_info', (), {}), - ('name', (), {}), - ('net_connections', (), {'kind': 'all'}), - ('nice', (), {}), - ('num_ctx_switches', (), {}), - ('num_threads', (), {}), - ('open_files', (), {}), - ('ppid', (), {}), - ('status', (), {}), - ('threads', (), {}), - ('username', (), {}), - ] - if POSIX: - getters += [('uids', (), {})] - getters += [('gids', (), {})] - getters += [('terminal', (), {})] - getters += [('num_fds', (), {})] - if HAS_PROC_IO_COUNTERS: - getters += [('io_counters', (), {})] - if HAS_IONICE: - getters += [('ionice', (), {})] - if HAS_RLIMIT: - getters += [('rlimit', (psutil.RLIMIT_NOFILE,), {})] - if HAS_CPU_AFFINITY: - getters += [('cpu_affinity', (), {})] - if HAS_PROC_CPU_NUM: - getters += [('cpu_num', (), {})] - if HAS_ENVIRON: - getters += [('environ', (), {})] - if WINDOWS: - getters += [('num_handles', (), {})] - if HAS_MEMORY_MAPS: - getters += [('memory_maps', (), {'grouped': False})] - - setters = [] - if POSIX: - setters += [('nice', (0,), {})] - else: - setters += [('nice', (psutil.NORMAL_PRIORITY_CLASS,), {})] - if HAS_RLIMIT: - setters += [('rlimit', (psutil.RLIMIT_NOFILE, (1024, 4096)), {})] - if HAS_IONICE: - if LINUX: - setters += [('ionice', (psutil.IOPRIO_CLASS_NONE, 0), {})] - else: - setters += [('ionice', (psutil.IOPRIO_NORMAL,), {})] - if HAS_CPU_AFFINITY: - setters += [('cpu_affinity', ([_get_eligible_cpu()],), {})] - - killers = [ - ('send_signal', (signal.SIGTERM,), {}), - ('suspend', (), {}), - ('resume', (), {}), - ('terminate', (), {}), - ('kill', (), {}), - ] - if WINDOWS: - killers += [('send_signal', (signal.CTRL_C_EVENT,), {})] - killers += [('send_signal', (signal.CTRL_BREAK_EVENT,), {})] - - all = utils + getters + setters + killers - - def __init__(self, proc): - self._proc = proc - - def iter(self, ls, clear_cache=True): - """Given a list of tuples yields a set of (fun, fun_name) tuples - in random order. - """ - ls = list(ls) - random.shuffle(ls) - for fun_name, args, kwds in ls: - if clear_cache: - self.clear_cache() - fun = getattr(self._proc, fun_name) - fun = functools.partial(fun, *args, **kwds) - yield (fun, fun_name) - - def clear_cache(self): - """Clear the cache of a Process instance.""" - self._proc._init(self._proc.pid, _ignore_nsp=True) - - @classmethod - def test_class_coverage(cls, test_class, ls): - """Given a TestCase instance and a list of tuples checks that - the class defines the required test method names. - """ - for fun_name, _, _ in ls: - meth_name = 'test_' + fun_name - if not hasattr(test_class, meth_name): - msg = ( - f"{test_class.__class__.__name__!r} class should define a" - f" {meth_name!r} method" - ) - raise AttributeError(msg) - - @classmethod - def test(cls): - this = {x[0] for x in cls.all} - ignored = {x[0] for x in cls.ignored} - klass = {x for x in dir(psutil.Process) if x[0] != '_'} - leftout = (this | ignored) ^ klass - if leftout: - raise ValueError(f"uncovered Process class names: {leftout!r}") - - -class system_namespace: - """A container that lists all the module-level, system-related APIs. - Utilities such as cpu_percent() are excluded. Usage: - - >>> ns = system_namespace - >>> for fun, name in ns.iter(ns.getters): - ... fun() - """ - - getters = [ - ('boot_time', (), {}), - ('cpu_count', (), {'logical': False}), - ('cpu_count', (), {'logical': True}), - ('cpu_stats', (), {}), - ('cpu_times', (), {'percpu': False}), - ('cpu_times', (), {'percpu': True}), - ('disk_io_counters', (), {'perdisk': True}), - ('disk_partitions', (), {'all': True}), - ('disk_usage', (os.getcwd(),), {}), - ('net_connections', (), {'kind': 'all'}), - ('net_if_addrs', (), {}), - ('net_if_stats', (), {}), - ('net_io_counters', (), {'pernic': True}), - ('pid_exists', (os.getpid(),), {}), - ('pids', (), {}), - ('swap_memory', (), {}), - ('users', (), {}), - ('virtual_memory', (), {}), - ] - if HAS_CPU_FREQ: - if MACOS and platform.machine() == 'arm64': # skipped due to #1892 - pass - else: - getters += [('cpu_freq', (), {'percpu': True})] - if HAS_GETLOADAVG: - getters += [('getloadavg', (), {})] - if HAS_SENSORS_TEMPERATURES: - getters += [('sensors_temperatures', (), {})] - if HAS_SENSORS_FANS: - getters += [('sensors_fans', (), {})] - if HAS_SENSORS_BATTERY: - getters += [('sensors_battery', (), {})] - if WINDOWS: - getters += [('win_service_iter', (), {})] - getters += [('win_service_get', ('alg',), {})] - - ignored = [ - ('process_iter', (), {}), - ('wait_procs', ([psutil.Process()],), {}), - ('cpu_percent', (), {}), - ('cpu_times_percent', (), {}), - ] - - all = getters - - @staticmethod - def iter(ls): - """Given a list of tuples yields a set of (fun, fun_name) tuples - in random order. - """ - ls = list(ls) - random.shuffle(ls) - for fun_name, args, kwds in ls: - fun = getattr(psutil, fun_name) - fun = functools.partial(fun, *args, **kwds) - yield (fun, fun_name) - - test_class_coverage = process_namespace.test_class_coverage - - -def retry_on_failure(retries=NO_RETRIES): - """Decorator which runs a test function and retries N times before - actually failing. - """ - - def logfun(exc): - print(f"{exc!r}, retrying", file=sys.stderr) # noqa: T201 - - return retry( - exception=AssertionError, timeout=None, retries=retries, logfun=logfun - ) - - -def skip_on_access_denied(only_if=None): - """Decorator to Ignore AccessDenied exceptions.""" - - def decorator(fun): - @functools.wraps(fun) - def wrapper(*args, **kwargs): - try: - return fun(*args, **kwargs) - except psutil.AccessDenied: - if only_if is not None: - if not only_if: - raise - raise pytest.skip("raises AccessDenied") - - return wrapper - - return decorator - - -def skip_on_not_implemented(only_if=None): - """Decorator to Ignore NotImplementedError exceptions.""" - - def decorator(fun): - @functools.wraps(fun) - def wrapper(*args, **kwargs): - try: - return fun(*args, **kwargs) - except NotImplementedError: - if only_if is not None: - if not only_if: - raise - msg = ( - f"{fun.__name__!r} was skipped because it raised" - " NotImplementedError" - ) - raise pytest.skip(msg) - - return wrapper - - return decorator - - -# =================================================================== -# --- network -# =================================================================== - - -# XXX: no longer used -def get_free_port(host='127.0.0.1'): - """Return an unused TCP port. Subject to race conditions.""" - with socket.socket() as sock: - sock.bind((host, 0)) - return sock.getsockname()[1] - - -def bind_socket(family=AF_INET, type=SOCK_STREAM, addr=None): - """Binds a generic socket.""" - if addr is None and family in {AF_INET, AF_INET6}: - addr = ("", 0) - sock = socket.socket(family, type) - try: - if os.name not in {'nt', 'cygwin'}: - sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - sock.bind(addr) - if type == socket.SOCK_STREAM: - sock.listen(5) - return sock - except Exception: - sock.close() - raise - - -def bind_unix_socket(name, type=socket.SOCK_STREAM): - """Bind a UNIX socket.""" - assert psutil.POSIX - assert not os.path.exists(name), name - sock = socket.socket(socket.AF_UNIX, type) - try: - sock.bind(name) - if type == socket.SOCK_STREAM: - sock.listen(5) - except Exception: - sock.close() - raise - return sock - - -def tcp_socketpair(family, addr=("", 0)): - """Build a pair of TCP sockets connected to each other. - Return a (server, client) tuple. - """ - with socket.socket(family, SOCK_STREAM) as ll: - ll.bind(addr) - ll.listen(5) - addr = ll.getsockname() - c = socket.socket(family, SOCK_STREAM) - try: - c.connect(addr) - caddr = c.getsockname() - while True: - a, addr = ll.accept() - # check that we've got the correct client - if addr == caddr: - return (a, c) - a.close() - except OSError: - c.close() - raise - - -def unix_socketpair(name): - """Build a pair of UNIX sockets connected to each other through - the same UNIX file name. - Return a (server, client) tuple. - """ - assert psutil.POSIX - server = client = None - try: - server = bind_unix_socket(name, type=socket.SOCK_STREAM) - server.setblocking(0) - client = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - client.setblocking(0) - client.connect(name) - # new = server.accept() - except Exception: - if server is not None: - server.close() - if client is not None: - client.close() - raise - return (server, client) - - -@contextlib.contextmanager -def create_sockets(): - """Open as many socket families / types as possible.""" - socks = [] - fname1 = fname2 = None - try: - socks.extend(( - bind_socket(socket.AF_INET, socket.SOCK_STREAM), - bind_socket(socket.AF_INET, socket.SOCK_DGRAM), - )) - if supports_ipv6(): - socks.extend(( - bind_socket(socket.AF_INET6, socket.SOCK_STREAM), - bind_socket(socket.AF_INET6, socket.SOCK_DGRAM), - )) - if POSIX and HAS_NET_CONNECTIONS_UNIX: - fname1 = get_testfn() - fname2 = get_testfn() - s1, s2 = unix_socketpair(fname1) - s3 = bind_unix_socket(fname2, type=socket.SOCK_DGRAM) - for s in (s1, s2, s3): - socks.append(s) - yield socks - finally: - for s in socks: - s.close() - for fname in (fname1, fname2): - if fname is not None: - safe_rmpath(fname) - - -def check_net_address(addr, family): - """Check a net address validity. Supported families are IPv4, - IPv6 and MAC addresses. - """ - assert isinstance(family, enum.IntEnum), family - if family == socket.AF_INET: - octs = [int(x) for x in addr.split('.')] - assert len(octs) == 4, addr - for num in octs: - assert 0 <= num <= 255, addr - ipaddress.IPv4Address(addr) - elif family == socket.AF_INET6: - assert isinstance(addr, str), addr - ipaddress.IPv6Address(addr) - elif family == psutil.AF_LINK: - assert re.match(r'([a-fA-F0-9]{2}[:|\-]?){6}', addr) is not None, addr - else: - raise ValueError(f"unknown family {family!r}") - - -def check_connection_ntuple(conn): - """Check validity of a connection namedtuple.""" - - def check_ntuple(conn): - has_pid = len(conn) == 7 - assert len(conn) in {6, 7}, len(conn) - assert conn[0] == conn.fd, conn.fd - assert conn[1] == conn.family, conn.family - assert conn[2] == conn.type, conn.type - assert conn[3] == conn.laddr, conn.laddr - assert conn[4] == conn.raddr, conn.raddr - assert conn[5] == conn.status, conn.status - if has_pid: - assert conn[6] == conn.pid, conn.pid - - def check_family(conn): - assert conn.family in {AF_INET, AF_INET6, AF_UNIX}, conn.family - assert isinstance(conn.family, enum.IntEnum), conn - if conn.family == AF_INET: - # actually try to bind the local socket; ignore IPv6 - # sockets as their address might be represented as - # an IPv4-mapped-address (e.g. "::127.0.0.1") - # and that's rejected by bind() - with socket.socket(conn.family, conn.type) as s: - try: - s.bind((conn.laddr[0], 0)) - except OSError as err: - if err.errno != errno.EADDRNOTAVAIL: - raise - elif conn.family == AF_UNIX: - assert conn.status == psutil.CONN_NONE, conn.status - - def check_type(conn): - # SOCK_SEQPACKET may happen in case of AF_UNIX socks - SOCK_SEQPACKET = getattr(socket, "SOCK_SEQPACKET", object()) - assert conn.type in { - socket.SOCK_STREAM, - socket.SOCK_DGRAM, - SOCK_SEQPACKET, - }, conn.type - assert isinstance(conn.type, enum.IntEnum), conn - if conn.type == socket.SOCK_DGRAM: - assert conn.status == psutil.CONN_NONE, conn.status - - def check_addrs(conn): - # check IP address and port sanity - for addr in (conn.laddr, conn.raddr): - if conn.family in {AF_INET, AF_INET6}: - assert isinstance(addr, tuple), type(addr) - if not addr: - continue - assert isinstance(addr.port, int), type(addr.port) - assert 0 <= addr.port <= 65535, addr.port - check_net_address(addr.ip, conn.family) - elif conn.family == AF_UNIX: - assert isinstance(addr, str), type(addr) - - def check_status(conn): - assert isinstance(conn.status, str), conn.status - valids = [ - getattr(psutil, x) for x in dir(psutil) if x.startswith('CONN_') - ] - assert conn.status in valids, conn.status - if conn.family in {AF_INET, AF_INET6} and conn.type == SOCK_STREAM: - assert conn.status != psutil.CONN_NONE, conn.status - else: - assert conn.status == psutil.CONN_NONE, conn.status - - check_ntuple(conn) - check_family(conn) - check_type(conn) - check_addrs(conn) - check_status(conn) - - -def filter_proc_net_connections(cons): - """Our process may start with some open UNIX sockets which are not - initialized by us, invalidating unit tests. - """ - new = [] - for conn in cons: - if POSIX and conn.family == socket.AF_UNIX: - if MACOS and "/syslog" in conn.raddr: - debug(f"skipping {conn}") - continue - new.append(conn) - return new - - -# =================================================================== -# --- import utils -# =================================================================== - - -def reload_module(module): - return importlib.reload(module) - - -def import_module_by_path(path): - name = os.path.splitext(os.path.basename(path))[0] - spec = importlib.util.spec_from_file_location(name, path) - mod = importlib.util.module_from_spec(spec) - spec.loader.exec_module(mod) - return mod - - -# =================================================================== -# --- others -# =================================================================== - - -def warn(msg): - """Raise a warning msg.""" - warnings.warn(msg, UserWarning, stacklevel=2) - - -def is_namedtuple(x): - """Check if object is an instance of namedtuple.""" - t = type(x) - b = t.__bases__ - if len(b) != 1 or b[0] is not tuple: - return False - f = getattr(t, '_fields', None) - if not isinstance(f, tuple): - return False - return all(isinstance(n, str) for n in f) - - -if POSIX: - - @contextlib.contextmanager - def copyload_shared_lib(suffix=""): - """Ctx manager which picks up a random shared CO lib used - by this process, copies it in another location and loads it - in memory via ctypes. Return the new absolutized path. - """ - exe = 'pypy' if PYPY else 'python' - ext = ".so" - dst = get_testfn(suffix=suffix + ext) - libs = [ - x.path - for x in psutil.Process().memory_maps() - if os.path.splitext(x.path)[1] == ext and exe in x.path.lower() - ] - src = random.choice(libs) - shutil.copyfile(src, dst) - try: - ctypes.CDLL(dst) - yield dst - finally: - safe_rmpath(dst) - -else: - - @contextlib.contextmanager - def copyload_shared_lib(suffix=""): - """Ctx manager which picks up a random shared DLL lib used - by this process, copies it in another location and loads it - in memory via ctypes. - Return the new absolutized, normcased path. - """ - from ctypes import WinError - from ctypes import wintypes - - ext = ".dll" - dst = get_testfn(suffix=suffix + ext) - libs = [ - x.path - for x in psutil.Process().memory_maps() - if x.path.lower().endswith(ext) - and 'python' in os.path.basename(x.path).lower() - and 'wow64' not in x.path.lower() - ] - if PYPY and not libs: - libs = [ - x.path - for x in psutil.Process().memory_maps() - if 'pypy' in os.path.basename(x.path).lower() - ] - src = random.choice(libs) - shutil.copyfile(src, dst) - cfile = None - try: - cfile = ctypes.WinDLL(dst) - yield dst - finally: - # Work around OverflowError: - # - https://ci.appveyor.com/project/giampaolo/psutil/build/1207/ - # job/o53330pbnri9bcw7 - # - http://bugs.python.org/issue30286 - # - http://stackoverflow.com/questions/23522055 - if cfile is not None: - FreeLibrary = ctypes.windll.kernel32.FreeLibrary - FreeLibrary.argtypes = [wintypes.HMODULE] - ret = FreeLibrary(cfile._handle) - if ret == 0: - raise WinError() - safe_rmpath(dst) - - -# =================================================================== -# --- Exit funs (first is executed last) -# =================================================================== - - -# this is executed first -@atexit.register -def cleanup_test_procs(): - reap_children(recursive=True) - - -# atexit module does not execute exit functions in case of SIGTERM, which -# gets sent to test subprocesses, which is a problem if they import this -# module. With this it will. See: -# https://gmpy.dev/blog/2016/how-to-always-execute-exit-functions-in-python -if POSIX: - signal.signal(signal.SIGTERM, lambda sig, _: sys.exit(sig)) diff --git a/PortablePython/Lib/site-packages/psutil/tests/__main__.py b/PortablePython/Lib/site-packages/psutil/tests/__main__.py deleted file mode 100644 index ce6fc24..0000000 --- a/PortablePython/Lib/site-packages/psutil/tests/__main__.py +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -"""Run unit tests. This is invoked by: -$ python -m psutil.tests. -""" - -from psutil.tests import pytest - - -pytest.main(["-v", "-s", "--tb=short"]) diff --git a/PortablePython/Lib/site-packages/psutil/tests/test_aix.py b/PortablePython/Lib/site-packages/psutil/tests/test_aix.py deleted file mode 100644 index 10934c1..0000000 --- a/PortablePython/Lib/site-packages/psutil/tests/test_aix.py +++ /dev/null @@ -1,142 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright (c) 2009, Giampaolo Rodola' -# Copyright (c) 2017, Arnon Yaari -# All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -"""AIX specific tests.""" - -import re - -import psutil -from psutil import AIX -from psutil.tests import PsutilTestCase -from psutil.tests import pytest -from psutil.tests import sh - - -@pytest.mark.skipif(not AIX, reason="AIX only") -class AIXSpecificTestCase(PsutilTestCase): - def test_virtual_memory(self): - out = sh('/usr/bin/svmon -O unit=KB') - re_pattern = r"memory\s*" - for field in [ - "size", - "inuse", - "free", - "pin", - "virtual", - "available", - "mmode", - ]: - re_pattern += rf"(?P<{field}>\S+)\s+" - matchobj = re.search(re_pattern, out) - - assert matchobj is not None - - KB = 1024 - total = int(matchobj.group("size")) * KB - available = int(matchobj.group("available")) * KB - used = int(matchobj.group("inuse")) * KB - free = int(matchobj.group("free")) * KB - - psutil_result = psutil.virtual_memory() - - # TOLERANCE_SYS_MEM from psutil.tests is not enough. For some reason - # we're seeing differences of ~1.2 MB. 2 MB is still a good tolerance - # when compared to GBs. - TOLERANCE_SYS_MEM = 2 * KB * KB # 2 MB - assert psutil_result.total == total - assert abs(psutil_result.used - used) < TOLERANCE_SYS_MEM - assert abs(psutil_result.available - available) < TOLERANCE_SYS_MEM - assert abs(psutil_result.free - free) < TOLERANCE_SYS_MEM - - def test_swap_memory(self): - out = sh('/usr/sbin/lsps -a') - # From the man page, "The size is given in megabytes" so we assume - # we'll always have 'MB' in the result - # TODO maybe try to use "swap -l" to check "used" too, but its units - # are not guaranteed to be "MB" so parsing may not be consistent - matchobj = re.search( - r"(?P\S+)\s+" - r"(?P\S+)\s+" - r"(?P\S+)\s+" - r"(?P\d+)MB", - out, - ) - - assert matchobj is not None - - total_mb = int(matchobj.group("size")) - MB = 1024**2 - psutil_result = psutil.swap_memory() - # we divide our result by MB instead of multiplying the lsps value by - # MB because lsps may round down, so we round down too - assert int(psutil_result.total / MB) == total_mb - - def test_cpu_stats(self): - out = sh('/usr/bin/mpstat -a') - - re_pattern = r"ALL\s*" - for field in [ - "min", - "maj", - "mpcs", - "mpcr", - "dev", - "soft", - "dec", - "ph", - "cs", - "ics", - "bound", - "rq", - "push", - "S3pull", - "S3grd", - "S0rd", - "S1rd", - "S2rd", - "S3rd", - "S4rd", - "S5rd", - "sysc", - ]: - re_pattern += rf"(?P<{field}>\S+)\s+" - matchobj = re.search(re_pattern, out) - - assert matchobj is not None - - # numbers are usually in the millions so 1000 is ok for tolerance - CPU_STATS_TOLERANCE = 1000 - psutil_result = psutil.cpu_stats() - assert ( - abs(psutil_result.ctx_switches - int(matchobj.group("cs"))) - < CPU_STATS_TOLERANCE - ) - assert ( - abs(psutil_result.syscalls - int(matchobj.group("sysc"))) - < CPU_STATS_TOLERANCE - ) - assert ( - abs(psutil_result.interrupts - int(matchobj.group("dev"))) - < CPU_STATS_TOLERANCE - ) - assert ( - abs(psutil_result.soft_interrupts - int(matchobj.group("soft"))) - < CPU_STATS_TOLERANCE - ) - - def test_cpu_count_logical(self): - out = sh('/usr/bin/mpstat -a') - mpstat_lcpu = int(re.search(r"lcpu=(\d+)", out).group(1)) - psutil_lcpu = psutil.cpu_count(logical=True) - assert mpstat_lcpu == psutil_lcpu - - def test_net_if_addrs_names(self): - out = sh('/etc/ifconfig -l') - ifconfig_names = set(out.split()) - psutil_names = set(psutil.net_if_addrs().keys()) - assert ifconfig_names == psutil_names diff --git a/PortablePython/Lib/site-packages/psutil/tests/test_bsd.py b/PortablePython/Lib/site-packages/psutil/tests/test_bsd.py deleted file mode 100644 index 2786c34..0000000 --- a/PortablePython/Lib/site-packages/psutil/tests/test_bsd.py +++ /dev/null @@ -1,593 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -# TODO: (FreeBSD) add test for comparing connections with 'sockstat' cmd. - - -"""Tests specific to all BSD platforms.""" - -import datetime -import os -import re -import shutil -import time - -import psutil -from psutil import BSD -from psutil import FREEBSD -from psutil import NETBSD -from psutil import OPENBSD -from psutil.tests import HAS_BATTERY -from psutil.tests import TOLERANCE_SYS_MEM -from psutil.tests import PsutilTestCase -from psutil.tests import pytest -from psutil.tests import retry_on_failure -from psutil.tests import sh -from psutil.tests import spawn_testproc -from psutil.tests import terminate - - -if BSD: - from psutil._psutil_posix import getpagesize - - PAGESIZE = getpagesize() - # muse requires root privileges - MUSE_AVAILABLE = os.getuid() == 0 and shutil.which("muse") -else: - PAGESIZE = None - MUSE_AVAILABLE = False - - -def sysctl(cmdline): - """Expects a sysctl command with an argument and parse the result - returning only the value of interest. - """ - result = sh("sysctl " + cmdline) - if FREEBSD: - result = result[result.find(": ") + 2 :] - elif OPENBSD or NETBSD: - result = result[result.find("=") + 1 :] - try: - return int(result) - except ValueError: - return result - - -def muse(field): - """Thin wrapper around 'muse' cmdline utility.""" - out = sh('muse') - for line in out.split('\n'): - if line.startswith(field): - break - else: - raise ValueError("line not found") - return int(line.split()[1]) - - -# ===================================================================== -# --- All BSD* -# ===================================================================== - - -@pytest.mark.skipif(not BSD, reason="BSD only") -class BSDTestCase(PsutilTestCase): - """Generic tests common to all BSD variants.""" - - @classmethod - def setUpClass(cls): - cls.pid = spawn_testproc().pid - - @classmethod - def tearDownClass(cls): - terminate(cls.pid) - - @pytest.mark.skipif(NETBSD, reason="-o lstart doesn't work on NETBSD") - def test_process_create_time(self): - output = sh(f"ps -o lstart -p {self.pid}") - start_ps = output.replace('STARTED', '').strip() - start_psutil = psutil.Process(self.pid).create_time() - start_psutil = time.strftime( - "%a %b %e %H:%M:%S %Y", time.localtime(start_psutil) - ) - assert start_ps == start_psutil - - def test_disks(self): - # test psutil.disk_usage() and psutil.disk_partitions() - # against "df -a" - def df(path): - out = sh(f'df -k "{path}"').strip() - lines = out.split('\n') - lines.pop(0) - line = lines.pop(0) - dev, total, used, free = line.split()[:4] - if dev == 'none': - dev = '' - total = int(total) * 1024 - used = int(used) * 1024 - free = int(free) * 1024 - return dev, total, used, free - - for part in psutil.disk_partitions(all=False): - usage = psutil.disk_usage(part.mountpoint) - dev, total, used, free = df(part.mountpoint) - assert part.device == dev - assert usage.total == total - # 10 MB tolerance - if abs(usage.free - free) > 10 * 1024 * 1024: - raise self.fail(f"psutil={usage.free}, df={free}") - if abs(usage.used - used) > 10 * 1024 * 1024: - raise self.fail(f"psutil={usage.used}, df={used}") - - @pytest.mark.skipif( - not shutil.which("sysctl"), reason="sysctl cmd not available" - ) - def test_cpu_count_logical(self): - syst = sysctl("hw.ncpu") - assert psutil.cpu_count(logical=True) == syst - - @pytest.mark.skipif( - not shutil.which("sysctl"), reason="sysctl cmd not available" - ) - @pytest.mark.skipif( - NETBSD, reason="skipped on NETBSD" # we check /proc/meminfo - ) - def test_virtual_memory_total(self): - num = sysctl('hw.physmem') - assert num == psutil.virtual_memory().total - - @pytest.mark.skipif( - not shutil.which("ifconfig"), reason="ifconfig cmd not available" - ) - def test_net_if_stats(self): - for name, stats in psutil.net_if_stats().items(): - try: - out = sh(f"ifconfig {name}") - except RuntimeError: - pass - else: - assert stats.isup == ('RUNNING' in out) - if "mtu" in out: - assert stats.mtu == int(re.findall(r'mtu (\d+)', out)[0]) - - -# ===================================================================== -# --- FreeBSD -# ===================================================================== - - -@pytest.mark.skipif(not FREEBSD, reason="FREEBSD only") -class FreeBSDPsutilTestCase(PsutilTestCase): - @classmethod - def setUpClass(cls): - cls.pid = spawn_testproc().pid - - @classmethod - def tearDownClass(cls): - terminate(cls.pid) - - @retry_on_failure() - def test_memory_maps(self): - out = sh(f"procstat -v {self.pid}") - maps = psutil.Process(self.pid).memory_maps(grouped=False) - lines = out.split('\n')[1:] - while lines: - line = lines.pop() - fields = line.split() - _, start, stop, _perms, res = fields[:5] - map = maps.pop() - assert f"{start}-{stop}" == map.addr - assert int(res) == map.rss - if not map.path.startswith('['): - assert fields[10] == map.path - - def test_exe(self): - out = sh(f"procstat -b {self.pid}") - assert psutil.Process(self.pid).exe() == out.split('\n')[1].split()[-1] - - def test_cmdline(self): - out = sh(f"procstat -c {self.pid}") - assert ' '.join(psutil.Process(self.pid).cmdline()) == ' '.join( - out.split('\n')[1].split()[2:] - ) - - def test_uids_gids(self): - out = sh(f"procstat -s {self.pid}") - euid, ruid, suid, egid, rgid, sgid = out.split('\n')[1].split()[2:8] - p = psutil.Process(self.pid) - uids = p.uids() - gids = p.gids() - assert uids.real == int(ruid) - assert uids.effective == int(euid) - assert uids.saved == int(suid) - assert gids.real == int(rgid) - assert gids.effective == int(egid) - assert gids.saved == int(sgid) - - @retry_on_failure() - def test_ctx_switches(self): - tested = [] - out = sh(f"procstat -r {self.pid}") - p = psutil.Process(self.pid) - for line in out.split('\n'): - line = line.lower().strip() - if ' voluntary context' in line: - pstat_value = int(line.split()[-1]) - psutil_value = p.num_ctx_switches().voluntary - assert pstat_value == psutil_value - tested.append(None) - elif ' involuntary context' in line: - pstat_value = int(line.split()[-1]) - psutil_value = p.num_ctx_switches().involuntary - assert pstat_value == psutil_value - tested.append(None) - if len(tested) != 2: - raise RuntimeError("couldn't find lines match in procstat out") - - @retry_on_failure() - def test_cpu_times(self): - tested = [] - out = sh(f"procstat -r {self.pid}") - p = psutil.Process(self.pid) - for line in out.split('\n'): - line = line.lower().strip() - if 'user time' in line: - pstat_value = float('0.' + line.split()[-1].split('.')[-1]) - psutil_value = p.cpu_times().user - assert pstat_value == psutil_value - tested.append(None) - elif 'system time' in line: - pstat_value = float('0.' + line.split()[-1].split('.')[-1]) - psutil_value = p.cpu_times().system - assert pstat_value == psutil_value - tested.append(None) - if len(tested) != 2: - raise RuntimeError("couldn't find lines match in procstat out") - - -@pytest.mark.skipif(not FREEBSD, reason="FREEBSD only") -class FreeBSDSystemTestCase(PsutilTestCase): - @staticmethod - def parse_swapinfo(): - # the last line is always the total - output = sh("swapinfo -k").splitlines()[-1] - parts = re.split(r'\s+', output) - - if not parts: - raise ValueError(f"Can't parse swapinfo: {output}") - - # the size is in 1k units, so multiply by 1024 - total, used, free = (int(p) * 1024 for p in parts[1:4]) - return total, used, free - - def test_cpu_frequency_against_sysctl(self): - # Currently only cpu 0 is frequency is supported in FreeBSD - # All other cores use the same frequency. - sensor = "dev.cpu.0.freq" - try: - sysctl_result = int(sysctl(sensor)) - except RuntimeError: - raise pytest.skip("frequencies not supported by kernel") - assert psutil.cpu_freq().current == sysctl_result - - sensor = "dev.cpu.0.freq_levels" - sysctl_result = sysctl(sensor) - # sysctl returns a string of the format: - # / /... - # Ordered highest available to lowest available. - max_freq = int(sysctl_result.split()[0].split("/")[0]) - min_freq = int(sysctl_result.split()[-1].split("/")[0]) - assert psutil.cpu_freq().max == max_freq - assert psutil.cpu_freq().min == min_freq - - # --- virtual_memory(); tests against sysctl - - @retry_on_failure() - def test_vmem_active(self): - syst = sysctl("vm.stats.vm.v_active_count") * PAGESIZE - assert abs(psutil.virtual_memory().active - syst) < TOLERANCE_SYS_MEM - - @retry_on_failure() - def test_vmem_inactive(self): - syst = sysctl("vm.stats.vm.v_inactive_count") * PAGESIZE - assert abs(psutil.virtual_memory().inactive - syst) < TOLERANCE_SYS_MEM - - @retry_on_failure() - def test_vmem_wired(self): - syst = sysctl("vm.stats.vm.v_wire_count") * PAGESIZE - assert abs(psutil.virtual_memory().wired - syst) < TOLERANCE_SYS_MEM - - @retry_on_failure() - def test_vmem_cached(self): - syst = sysctl("vm.stats.vm.v_cache_count") * PAGESIZE - assert abs(psutil.virtual_memory().cached - syst) < TOLERANCE_SYS_MEM - - @retry_on_failure() - def test_vmem_free(self): - syst = sysctl("vm.stats.vm.v_free_count") * PAGESIZE - assert abs(psutil.virtual_memory().free - syst) < TOLERANCE_SYS_MEM - - @retry_on_failure() - def test_vmem_buffers(self): - syst = sysctl("vfs.bufspace") - assert abs(psutil.virtual_memory().buffers - syst) < TOLERANCE_SYS_MEM - - # --- virtual_memory(); tests against muse - - @pytest.mark.skipif(not MUSE_AVAILABLE, reason="muse not installed") - def test_muse_vmem_total(self): - num = muse('Total') - assert psutil.virtual_memory().total == num - - @pytest.mark.skipif(not MUSE_AVAILABLE, reason="muse not installed") - @retry_on_failure() - def test_muse_vmem_active(self): - num = muse('Active') - assert abs(psutil.virtual_memory().active - num) < TOLERANCE_SYS_MEM - - @pytest.mark.skipif(not MUSE_AVAILABLE, reason="muse not installed") - @retry_on_failure() - def test_muse_vmem_inactive(self): - num = muse('Inactive') - assert abs(psutil.virtual_memory().inactive - num) < TOLERANCE_SYS_MEM - - @pytest.mark.skipif(not MUSE_AVAILABLE, reason="muse not installed") - @retry_on_failure() - def test_muse_vmem_wired(self): - num = muse('Wired') - assert abs(psutil.virtual_memory().wired - num) < TOLERANCE_SYS_MEM - - @pytest.mark.skipif(not MUSE_AVAILABLE, reason="muse not installed") - @retry_on_failure() - def test_muse_vmem_cached(self): - num = muse('Cache') - assert abs(psutil.virtual_memory().cached - num) < TOLERANCE_SYS_MEM - - @pytest.mark.skipif(not MUSE_AVAILABLE, reason="muse not installed") - @retry_on_failure() - def test_muse_vmem_free(self): - num = muse('Free') - assert abs(psutil.virtual_memory().free - num) < TOLERANCE_SYS_MEM - - @pytest.mark.skipif(not MUSE_AVAILABLE, reason="muse not installed") - @retry_on_failure() - def test_muse_vmem_buffers(self): - num = muse('Buffer') - assert abs(psutil.virtual_memory().buffers - num) < TOLERANCE_SYS_MEM - - def test_cpu_stats_ctx_switches(self): - assert ( - abs( - psutil.cpu_stats().ctx_switches - - sysctl('vm.stats.sys.v_swtch') - ) - < 1000 - ) - - def test_cpu_stats_interrupts(self): - assert ( - abs(psutil.cpu_stats().interrupts - sysctl('vm.stats.sys.v_intr')) - < 1000 - ) - - def test_cpu_stats_soft_interrupts(self): - assert ( - abs( - psutil.cpu_stats().soft_interrupts - - sysctl('vm.stats.sys.v_soft') - ) - < 1000 - ) - - @retry_on_failure() - def test_cpu_stats_syscalls(self): - # pretty high tolerance but it looks like it's OK. - assert ( - abs(psutil.cpu_stats().syscalls - sysctl('vm.stats.sys.v_syscall')) - < 200000 - ) - - # def test_cpu_stats_traps(self): - # self.assertAlmostEqual(psutil.cpu_stats().traps, - # sysctl('vm.stats.sys.v_trap'), delta=1000) - - # --- swap memory - - def test_swapmem_free(self): - _total, _used, free = self.parse_swapinfo() - assert abs(psutil.swap_memory().free - free) < TOLERANCE_SYS_MEM - - def test_swapmem_used(self): - _total, used, _free = self.parse_swapinfo() - assert abs(psutil.swap_memory().used - used) < TOLERANCE_SYS_MEM - - def test_swapmem_total(self): - total, _used, _free = self.parse_swapinfo() - assert abs(psutil.swap_memory().total - total) < TOLERANCE_SYS_MEM - - # --- others - - def test_boot_time(self): - s = sysctl('sysctl kern.boottime') - s = s[s.find(" sec = ") + 7 :] - s = s[: s.find(',')] - btime = int(s) - assert btime == psutil.boot_time() - - # --- sensors_battery - - @pytest.mark.skipif(not HAS_BATTERY, reason="no battery") - def test_sensors_battery(self): - def secs2hours(secs): - m, _s = divmod(secs, 60) - h, m = divmod(m, 60) - return f"{int(h)}:{int(m):02}" - - out = sh("acpiconf -i 0") - fields = {x.split('\t')[0]: x.split('\t')[-1] for x in out.split("\n")} - metrics = psutil.sensors_battery() - percent = int(fields['Remaining capacity:'].replace('%', '')) - remaining_time = fields['Remaining time:'] - assert metrics.percent == percent - if remaining_time == 'unknown': - assert metrics.secsleft == psutil.POWER_TIME_UNLIMITED - else: - assert secs2hours(metrics.secsleft) == remaining_time - - @pytest.mark.skipif(not HAS_BATTERY, reason="no battery") - def test_sensors_battery_against_sysctl(self): - assert psutil.sensors_battery().percent == sysctl( - "hw.acpi.battery.life" - ) - assert psutil.sensors_battery().power_plugged == ( - sysctl("hw.acpi.acline") == 1 - ) - secsleft = psutil.sensors_battery().secsleft - if secsleft < 0: - assert sysctl("hw.acpi.battery.time") == -1 - else: - assert secsleft == sysctl("hw.acpi.battery.time") * 60 - - @pytest.mark.skipif(HAS_BATTERY, reason="has battery") - def test_sensors_battery_no_battery(self): - # If no battery is present one of these calls is supposed - # to fail, see: - # https://github.com/giampaolo/psutil/issues/1074 - with pytest.raises(RuntimeError): - sysctl("hw.acpi.battery.life") - sysctl("hw.acpi.battery.time") - sysctl("hw.acpi.acline") - assert psutil.sensors_battery() is None - - # --- sensors_temperatures - - def test_sensors_temperatures_against_sysctl(self): - num_cpus = psutil.cpu_count(True) - for cpu in range(num_cpus): - sensor = f"dev.cpu.{cpu}.temperature" - # sysctl returns a string in the format 46.0C - try: - sysctl_result = int(float(sysctl(sensor)[:-1])) - except RuntimeError: - raise pytest.skip("temperatures not supported by kernel") - assert ( - abs( - psutil.sensors_temperatures()["coretemp"][cpu].current - - sysctl_result - ) - < 10 - ) - - sensor = f"dev.cpu.{cpu}.coretemp.tjmax" - sysctl_result = int(float(sysctl(sensor)[:-1])) - assert ( - psutil.sensors_temperatures()["coretemp"][cpu].high - == sysctl_result - ) - - -# ===================================================================== -# --- OpenBSD -# ===================================================================== - - -@pytest.mark.skipif(not OPENBSD, reason="OPENBSD only") -class OpenBSDTestCase(PsutilTestCase): - def test_boot_time(self): - s = sysctl('kern.boottime') - sys_bt = datetime.datetime.strptime(s, "%a %b %d %H:%M:%S %Y") - psutil_bt = datetime.datetime.fromtimestamp(psutil.boot_time()) - assert sys_bt == psutil_bt - - -# ===================================================================== -# --- NetBSD -# ===================================================================== - - -@pytest.mark.skipif(not NETBSD, reason="NETBSD only") -class NetBSDTestCase(PsutilTestCase): - @staticmethod - def parse_meminfo(look_for): - with open('/proc/meminfo') as f: - for line in f: - if line.startswith(look_for): - return int(line.split()[1]) * 1024 - raise ValueError(f"can't find {look_for}") - - # --- virtual mem - - def test_vmem_total(self): - assert psutil.virtual_memory().total == self.parse_meminfo("MemTotal:") - - def test_vmem_free(self): - assert ( - abs(psutil.virtual_memory().free - self.parse_meminfo("MemFree:")) - < TOLERANCE_SYS_MEM - ) - - def test_vmem_buffers(self): - assert ( - abs( - psutil.virtual_memory().buffers - - self.parse_meminfo("Buffers:") - ) - < TOLERANCE_SYS_MEM - ) - - def test_vmem_shared(self): - assert ( - abs( - psutil.virtual_memory().shared - - self.parse_meminfo("MemShared:") - ) - < TOLERANCE_SYS_MEM - ) - - def test_vmem_cached(self): - assert ( - abs(psutil.virtual_memory().cached - self.parse_meminfo("Cached:")) - < TOLERANCE_SYS_MEM - ) - - # --- swap mem - - def test_swapmem_total(self): - assert ( - abs(psutil.swap_memory().total - self.parse_meminfo("SwapTotal:")) - < TOLERANCE_SYS_MEM - ) - - def test_swapmem_free(self): - assert ( - abs(psutil.swap_memory().free - self.parse_meminfo("SwapFree:")) - < TOLERANCE_SYS_MEM - ) - - def test_swapmem_used(self): - smem = psutil.swap_memory() - assert smem.used == smem.total - smem.free - - # --- others - - def test_cpu_stats_interrupts(self): - with open('/proc/stat', 'rb') as f: - for line in f: - if line.startswith(b'intr'): - interrupts = int(line.split()[1]) - break - else: - raise ValueError("couldn't find line") - assert abs(psutil.cpu_stats().interrupts - interrupts) < 1000 - - def test_cpu_stats_ctx_switches(self): - with open('/proc/stat', 'rb') as f: - for line in f: - if line.startswith(b'ctxt'): - ctx_switches = int(line.split()[1]) - break - else: - raise ValueError("couldn't find line") - assert abs(psutil.cpu_stats().ctx_switches - ctx_switches) < 1000 diff --git a/PortablePython/Lib/site-packages/psutil/tests/test_connections.py b/PortablePython/Lib/site-packages/psutil/tests/test_connections.py deleted file mode 100644 index 5ddeb85..0000000 --- a/PortablePython/Lib/site-packages/psutil/tests/test_connections.py +++ /dev/null @@ -1,566 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -"""Tests for psutil.net_connections() and Process.net_connections() APIs.""" - -import os -import socket -import textwrap -from contextlib import closing -from socket import AF_INET -from socket import AF_INET6 -from socket import SOCK_DGRAM -from socket import SOCK_STREAM - -import psutil -from psutil import FREEBSD -from psutil import LINUX -from psutil import MACOS -from psutil import NETBSD -from psutil import OPENBSD -from psutil import POSIX -from psutil import SUNOS -from psutil import WINDOWS -from psutil._common import supports_ipv6 -from psutil.tests import AF_UNIX -from psutil.tests import HAS_NET_CONNECTIONS_UNIX -from psutil.tests import SKIP_SYSCONS -from psutil.tests import PsutilTestCase -from psutil.tests import bind_socket -from psutil.tests import bind_unix_socket -from psutil.tests import check_connection_ntuple -from psutil.tests import create_sockets -from psutil.tests import filter_proc_net_connections -from psutil.tests import pytest -from psutil.tests import reap_children -from psutil.tests import retry_on_failure -from psutil.tests import skip_on_access_denied -from psutil.tests import tcp_socketpair -from psutil.tests import unix_socketpair -from psutil.tests import wait_for_file - - -SOCK_SEQPACKET = getattr(socket, "SOCK_SEQPACKET", object()) - - -def this_proc_net_connections(kind): - cons = psutil.Process().net_connections(kind=kind) - if kind in {"all", "unix"}: - return filter_proc_net_connections(cons) - return cons - - -@pytest.mark.xdist_group(name="serial") -class ConnectionTestCase(PsutilTestCase): - def setUp(self): - assert this_proc_net_connections(kind='all') == [] - - def tearDown(self): - # Make sure we closed all resources. - assert this_proc_net_connections(kind='all') == [] - - def compare_procsys_connections(self, pid, proc_cons, kind='all'): - """Given a process PID and its list of connections compare - those against system-wide connections retrieved via - psutil.net_connections. - """ - try: - sys_cons = psutil.net_connections(kind=kind) - except psutil.AccessDenied: - # On MACOS, system-wide connections are retrieved by iterating - # over all processes - if MACOS: - return - else: - raise - # Filter for this proc PID and exlucde PIDs from the tuple. - sys_cons = [c[:-1] for c in sys_cons if c.pid == pid] - sys_cons.sort() - proc_cons.sort() - assert proc_cons == sys_cons - - -class TestBasicOperations(ConnectionTestCase): - @pytest.mark.skipif(SKIP_SYSCONS, reason="requires root") - def test_system(self): - with create_sockets(): - for conn in psutil.net_connections(kind='all'): - check_connection_ntuple(conn) - - def test_process(self): - with create_sockets(): - for conn in this_proc_net_connections(kind='all'): - check_connection_ntuple(conn) - - def test_invalid_kind(self): - with pytest.raises(ValueError): - this_proc_net_connections(kind='???') - with pytest.raises(ValueError): - psutil.net_connections(kind='???') - - -@pytest.mark.xdist_group(name="serial") -class TestUnconnectedSockets(ConnectionTestCase): - """Tests sockets which are open but not connected to anything.""" - - def get_conn_from_sock(self, sock): - cons = this_proc_net_connections(kind='all') - smap = {c.fd: c for c in cons} - if NETBSD or FREEBSD: - # NetBSD opens a UNIX socket to /var/log/run - # so there may be more connections. - return smap[sock.fileno()] - else: - assert len(cons) == 1 - if cons[0].fd != -1: - assert smap[sock.fileno()].fd == sock.fileno() - return cons[0] - - def check_socket(self, sock): - """Given a socket, makes sure it matches the one obtained - via psutil. It assumes this process created one connection - only (the one supposed to be checked). - """ - conn = self.get_conn_from_sock(sock) - check_connection_ntuple(conn) - - # fd, family, type - if conn.fd != -1: - assert conn.fd == sock.fileno() - assert conn.family == sock.family - # see: http://bugs.python.org/issue30204 - assert conn.type == sock.getsockopt(socket.SOL_SOCKET, socket.SO_TYPE) - - # local address - laddr = sock.getsockname() - if not laddr and isinstance(laddr, bytes): - # See: http://bugs.python.org/issue30205 - laddr = laddr.decode() - if sock.family == AF_INET6: - laddr = laddr[:2] - assert conn.laddr == laddr - - # XXX Solaris can't retrieve system-wide UNIX sockets - if sock.family == AF_UNIX and HAS_NET_CONNECTIONS_UNIX: - cons = this_proc_net_connections(kind='all') - self.compare_procsys_connections(os.getpid(), cons, kind='all') - return conn - - def test_tcp_v4(self): - addr = ("127.0.0.1", 0) - with closing(bind_socket(AF_INET, SOCK_STREAM, addr=addr)) as sock: - conn = self.check_socket(sock) - assert conn.raddr == () - assert conn.status == psutil.CONN_LISTEN - - @pytest.mark.skipif(not supports_ipv6(), reason="IPv6 not supported") - def test_tcp_v6(self): - addr = ("::1", 0) - with closing(bind_socket(AF_INET6, SOCK_STREAM, addr=addr)) as sock: - conn = self.check_socket(sock) - assert conn.raddr == () - assert conn.status == psutil.CONN_LISTEN - - def test_udp_v4(self): - addr = ("127.0.0.1", 0) - with closing(bind_socket(AF_INET, SOCK_DGRAM, addr=addr)) as sock: - conn = self.check_socket(sock) - assert conn.raddr == () - assert conn.status == psutil.CONN_NONE - - @pytest.mark.skipif(not supports_ipv6(), reason="IPv6 not supported") - def test_udp_v6(self): - addr = ("::1", 0) - with closing(bind_socket(AF_INET6, SOCK_DGRAM, addr=addr)) as sock: - conn = self.check_socket(sock) - assert conn.raddr == () - assert conn.status == psutil.CONN_NONE - - @pytest.mark.skipif(not POSIX, reason="POSIX only") - def test_unix_tcp(self): - testfn = self.get_testfn() - with closing(bind_unix_socket(testfn, type=SOCK_STREAM)) as sock: - conn = self.check_socket(sock) - assert conn.raddr == "" - assert conn.status == psutil.CONN_NONE - - @pytest.mark.skipif(not POSIX, reason="POSIX only") - def test_unix_udp(self): - testfn = self.get_testfn() - with closing(bind_unix_socket(testfn, type=SOCK_STREAM)) as sock: - conn = self.check_socket(sock) - assert conn.raddr == "" - assert conn.status == psutil.CONN_NONE - - -@pytest.mark.xdist_group(name="serial") -class TestConnectedSocket(ConnectionTestCase): - """Test socket pairs which are actually connected to - each other. - """ - - # On SunOS, even after we close() it, the server socket stays around - # in TIME_WAIT state. - @pytest.mark.skipif(SUNOS, reason="unreliable on SUONS") - def test_tcp(self): - addr = ("127.0.0.1", 0) - assert this_proc_net_connections(kind='tcp4') == [] - server, client = tcp_socketpair(AF_INET, addr=addr) - try: - cons = this_proc_net_connections(kind='tcp4') - assert len(cons) == 2 - assert cons[0].status == psutil.CONN_ESTABLISHED - assert cons[1].status == psutil.CONN_ESTABLISHED - # May not be fast enough to change state so it stays - # commenteed. - # client.close() - # cons = this_proc_net_connections(kind='all') - # self.assertEqual(len(cons), 1) - # self.assertEqual(cons[0].status, psutil.CONN_CLOSE_WAIT) - finally: - server.close() - client.close() - - @pytest.mark.skipif(not POSIX, reason="POSIX only") - def test_unix(self): - testfn = self.get_testfn() - server, client = unix_socketpair(testfn) - try: - cons = this_proc_net_connections(kind='unix') - assert not (cons[0].laddr and cons[0].raddr), cons - assert not (cons[1].laddr and cons[1].raddr), cons - if NETBSD or FREEBSD: - # On NetBSD creating a UNIX socket will cause - # a UNIX connection to /var/run/log. - cons = [c for c in cons if c.raddr != '/var/run/log'] - assert len(cons) == 2 - if LINUX or FREEBSD or SUNOS or OPENBSD: - # remote path is never set - assert cons[0].raddr == "" - assert cons[1].raddr == "" - # one local address should though - assert testfn == (cons[0].laddr or cons[1].laddr) - else: - # On other systems either the laddr or raddr - # of both peers are set. - assert (cons[0].laddr or cons[1].laddr) == testfn - finally: - server.close() - client.close() - - -class TestFilters(ConnectionTestCase): - def test_filters(self): - def check(kind, families, types): - for conn in this_proc_net_connections(kind=kind): - assert conn.family in families - assert conn.type in types - if not SKIP_SYSCONS: - for conn in psutil.net_connections(kind=kind): - assert conn.family in families - assert conn.type in types - - with create_sockets(): - check( - 'all', - [AF_INET, AF_INET6, AF_UNIX], - [SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET], - ) - check('inet', [AF_INET, AF_INET6], [SOCK_STREAM, SOCK_DGRAM]) - check('inet4', [AF_INET], [SOCK_STREAM, SOCK_DGRAM]) - check('tcp', [AF_INET, AF_INET6], [SOCK_STREAM]) - check('tcp4', [AF_INET], [SOCK_STREAM]) - check('tcp6', [AF_INET6], [SOCK_STREAM]) - check('udp', [AF_INET, AF_INET6], [SOCK_DGRAM]) - check('udp4', [AF_INET], [SOCK_DGRAM]) - check('udp6', [AF_INET6], [SOCK_DGRAM]) - if HAS_NET_CONNECTIONS_UNIX: - check( - 'unix', - [AF_UNIX], - [SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET], - ) - - @skip_on_access_denied(only_if=MACOS) - def test_combos(self): - reap_children() - - def check_conn(proc, conn, family, type, laddr, raddr, status, kinds): - all_kinds = ( - "all", - "inet", - "inet4", - "inet6", - "tcp", - "tcp4", - "tcp6", - "udp", - "udp4", - "udp6", - ) - check_connection_ntuple(conn) - assert conn.family == family - assert conn.type == type - assert conn.laddr == laddr - assert conn.raddr == raddr - assert conn.status == status - for kind in all_kinds: - cons = proc.net_connections(kind=kind) - if kind in kinds: - assert cons != [] - else: - assert cons == [] - # compare against system-wide connections - # XXX Solaris can't retrieve system-wide UNIX - # sockets. - if HAS_NET_CONNECTIONS_UNIX: - self.compare_procsys_connections(proc.pid, [conn]) - - tcp_template = textwrap.dedent(""" - import socket, time - s = socket.socket({family}, socket.SOCK_STREAM) - s.bind(('{addr}', 0)) - s.listen(5) - with open('{testfn}', 'w') as f: - f.write(str(s.getsockname()[:2])) - [time.sleep(0.1) for x in range(100)] - """) - - udp_template = textwrap.dedent(""" - import socket, time - s = socket.socket({family}, socket.SOCK_DGRAM) - s.bind(('{addr}', 0)) - with open('{testfn}', 'w') as f: - f.write(str(s.getsockname()[:2])) - [time.sleep(0.1) for x in range(100)] - """) - - # must be relative on Windows - testfile = os.path.basename(self.get_testfn(dir=os.getcwd())) - tcp4_template = tcp_template.format( - family=int(AF_INET), addr="127.0.0.1", testfn=testfile - ) - udp4_template = udp_template.format( - family=int(AF_INET), addr="127.0.0.1", testfn=testfile - ) - tcp6_template = tcp_template.format( - family=int(AF_INET6), addr="::1", testfn=testfile - ) - udp6_template = udp_template.format( - family=int(AF_INET6), addr="::1", testfn=testfile - ) - - # launch various subprocess instantiating a socket of various - # families and types to enrich psutil results - tcp4_proc = self.pyrun(tcp4_template) - tcp4_addr = eval(wait_for_file(testfile, delete=True)) - udp4_proc = self.pyrun(udp4_template) - udp4_addr = eval(wait_for_file(testfile, delete=True)) - if supports_ipv6(): - tcp6_proc = self.pyrun(tcp6_template) - tcp6_addr = eval(wait_for_file(testfile, delete=True)) - udp6_proc = self.pyrun(udp6_template) - udp6_addr = eval(wait_for_file(testfile, delete=True)) - else: - tcp6_proc = None - udp6_proc = None - tcp6_addr = None - udp6_addr = None - - for p in psutil.Process().children(): - cons = p.net_connections() - assert len(cons) == 1 - for conn in cons: - # TCP v4 - if p.pid == tcp4_proc.pid: - check_conn( - p, - conn, - AF_INET, - SOCK_STREAM, - tcp4_addr, - (), - psutil.CONN_LISTEN, - ("all", "inet", "inet4", "tcp", "tcp4"), - ) - # UDP v4 - elif p.pid == udp4_proc.pid: - check_conn( - p, - conn, - AF_INET, - SOCK_DGRAM, - udp4_addr, - (), - psutil.CONN_NONE, - ("all", "inet", "inet4", "udp", "udp4"), - ) - # TCP v6 - elif p.pid == getattr(tcp6_proc, "pid", None): - check_conn( - p, - conn, - AF_INET6, - SOCK_STREAM, - tcp6_addr, - (), - psutil.CONN_LISTEN, - ("all", "inet", "inet6", "tcp", "tcp6"), - ) - # UDP v6 - elif p.pid == getattr(udp6_proc, "pid", None): - check_conn( - p, - conn, - AF_INET6, - SOCK_DGRAM, - udp6_addr, - (), - psutil.CONN_NONE, - ("all", "inet", "inet6", "udp", "udp6"), - ) - - def test_count(self): - with create_sockets(): - # tcp - cons = this_proc_net_connections(kind='tcp') - assert len(cons) == (2 if supports_ipv6() else 1) - for conn in cons: - assert conn.family in {AF_INET, AF_INET6} - assert conn.type == SOCK_STREAM - # tcp4 - cons = this_proc_net_connections(kind='tcp4') - assert len(cons) == 1 - assert cons[0].family == AF_INET - assert cons[0].type == SOCK_STREAM - # tcp6 - if supports_ipv6(): - cons = this_proc_net_connections(kind='tcp6') - assert len(cons) == 1 - assert cons[0].family == AF_INET6 - assert cons[0].type == SOCK_STREAM - # udp - cons = this_proc_net_connections(kind='udp') - assert len(cons) == (2 if supports_ipv6() else 1) - for conn in cons: - assert conn.family in {AF_INET, AF_INET6} - assert conn.type == SOCK_DGRAM - # udp4 - cons = this_proc_net_connections(kind='udp4') - assert len(cons) == 1 - assert cons[0].family == AF_INET - assert cons[0].type == SOCK_DGRAM - # udp6 - if supports_ipv6(): - cons = this_proc_net_connections(kind='udp6') - assert len(cons) == 1 - assert cons[0].family == AF_INET6 - assert cons[0].type == SOCK_DGRAM - # inet - cons = this_proc_net_connections(kind='inet') - assert len(cons) == (4 if supports_ipv6() else 2) - for conn in cons: - assert conn.family in {AF_INET, AF_INET6} - assert conn.type in {SOCK_STREAM, SOCK_DGRAM} - # inet6 - if supports_ipv6(): - cons = this_proc_net_connections(kind='inet6') - assert len(cons) == 2 - for conn in cons: - assert conn.family == AF_INET6 - assert conn.type in {SOCK_STREAM, SOCK_DGRAM} - # Skipped on BSD becayse by default the Python process - # creates a UNIX socket to '/var/run/log'. - if HAS_NET_CONNECTIONS_UNIX and not (FREEBSD or NETBSD): - cons = this_proc_net_connections(kind='unix') - assert len(cons) == 3 - for conn in cons: - assert conn.family == AF_UNIX - assert conn.type in {SOCK_STREAM, SOCK_DGRAM} - - -@pytest.mark.skipif(SKIP_SYSCONS, reason="requires root") -class TestSystemWideConnections(ConnectionTestCase): - """Tests for net_connections().""" - - def test_it(self): - def check(cons, families, types_): - for conn in cons: - assert conn.family in families - if conn.family != AF_UNIX: - assert conn.type in types_ - check_connection_ntuple(conn) - - with create_sockets(): - from psutil._common import conn_tmap - - for kind, groups in conn_tmap.items(): - # XXX: SunOS does not retrieve UNIX sockets. - if kind == 'unix' and not HAS_NET_CONNECTIONS_UNIX: - continue - families, types_ = groups - cons = psutil.net_connections(kind) - assert len(cons) == len(set(cons)) - check(cons, families, types_) - - @retry_on_failure() - def test_multi_sockets_procs(self): - # Creates multiple sub processes, each creating different - # sockets. For each process check that proc.net_connections() - # and psutil.net_connections() return the same results. - # This is done mainly to check whether net_connections()'s - # pid is properly set, see: - # https://github.com/giampaolo/psutil/issues/1013 - with create_sockets() as socks: - expected = len(socks) - pids = [] - times = 10 - fnames = [] - for _ in range(times): - fname = self.get_testfn() - fnames.append(fname) - src = textwrap.dedent(f"""\ - import time, os - from psutil.tests import create_sockets - with create_sockets(): - with open(r'{fname}', 'w') as f: - f.write("hello") - [time.sleep(0.1) for x in range(100)] - """) - sproc = self.pyrun(src) - pids.append(sproc.pid) - - # sync - for fname in fnames: - wait_for_file(fname) - - syscons = [ - x for x in psutil.net_connections(kind='all') if x.pid in pids - ] - for pid in pids: - assert len([x for x in syscons if x.pid == pid]) == expected - p = psutil.Process(pid) - assert len(p.net_connections('all')) == expected - - -class TestMisc(PsutilTestCase): - def test_net_connection_constants(self): - ints = [] - strs = [] - for name in dir(psutil): - if name.startswith('CONN_'): - num = getattr(psutil, name) - str_ = str(num) - assert str_.isupper(), str_ - assert str not in strs - assert num not in ints - ints.append(num) - strs.append(str_) - if SUNOS: - psutil.CONN_IDLE # noqa: B018 - psutil.CONN_BOUND # noqa: B018 - if WINDOWS: - psutil.CONN_DELETE_TCB # noqa: B018 diff --git a/PortablePython/Lib/site-packages/psutil/tests/test_contracts.py b/PortablePython/Lib/site-packages/psutil/tests/test_contracts.py deleted file mode 100644 index 55f3a5d..0000000 --- a/PortablePython/Lib/site-packages/psutil/tests/test_contracts.py +++ /dev/null @@ -1,325 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -"""Contracts tests. These tests mainly check API sanity in terms of -returned types and APIs availability. -Some of these are duplicates of tests test_system.py and test_process.py. -""" - -import platform -import signal - -import psutil -from psutil import AIX -from psutil import FREEBSD -from psutil import LINUX -from psutil import MACOS -from psutil import NETBSD -from psutil import OPENBSD -from psutil import POSIX -from psutil import SUNOS -from psutil import WINDOWS -from psutil.tests import GITHUB_ACTIONS -from psutil.tests import HAS_CPU_FREQ -from psutil.tests import HAS_NET_IO_COUNTERS -from psutil.tests import HAS_SENSORS_FANS -from psutil.tests import HAS_SENSORS_TEMPERATURES -from psutil.tests import SKIP_SYSCONS -from psutil.tests import PsutilTestCase -from psutil.tests import create_sockets -from psutil.tests import enum -from psutil.tests import is_namedtuple -from psutil.tests import kernel_version -from psutil.tests import pytest - - -# =================================================================== -# --- APIs availability -# =================================================================== - -# Make sure code reflects what doc promises in terms of APIs -# availability. - - -class TestAvailConstantsAPIs(PsutilTestCase): - def test_PROCFS_PATH(self): - assert hasattr(psutil, "PROCFS_PATH") == (LINUX or SUNOS or AIX) - - def test_win_priority(self): - ae = self.assertEqual - ae(hasattr(psutil, "ABOVE_NORMAL_PRIORITY_CLASS"), WINDOWS) - ae(hasattr(psutil, "BELOW_NORMAL_PRIORITY_CLASS"), WINDOWS) - ae(hasattr(psutil, "HIGH_PRIORITY_CLASS"), WINDOWS) - ae(hasattr(psutil, "IDLE_PRIORITY_CLASS"), WINDOWS) - ae(hasattr(psutil, "NORMAL_PRIORITY_CLASS"), WINDOWS) - ae(hasattr(psutil, "REALTIME_PRIORITY_CLASS"), WINDOWS) - - def test_linux_ioprio_linux(self): - ae = self.assertEqual - ae(hasattr(psutil, "IOPRIO_CLASS_NONE"), LINUX) - ae(hasattr(psutil, "IOPRIO_CLASS_RT"), LINUX) - ae(hasattr(psutil, "IOPRIO_CLASS_BE"), LINUX) - ae(hasattr(psutil, "IOPRIO_CLASS_IDLE"), LINUX) - - def test_linux_ioprio_windows(self): - ae = self.assertEqual - ae(hasattr(psutil, "IOPRIO_HIGH"), WINDOWS) - ae(hasattr(psutil, "IOPRIO_NORMAL"), WINDOWS) - ae(hasattr(psutil, "IOPRIO_LOW"), WINDOWS) - ae(hasattr(psutil, "IOPRIO_VERYLOW"), WINDOWS) - - @pytest.mark.skipif( - GITHUB_ACTIONS and LINUX, - reason="unsupported on GITHUB_ACTIONS + LINUX", - ) - def test_rlimit(self): - ae = self.assertEqual - ae(hasattr(psutil, "RLIM_INFINITY"), LINUX or FREEBSD) - ae(hasattr(psutil, "RLIMIT_AS"), LINUX or FREEBSD) - ae(hasattr(psutil, "RLIMIT_CORE"), LINUX or FREEBSD) - ae(hasattr(psutil, "RLIMIT_CPU"), LINUX or FREEBSD) - ae(hasattr(psutil, "RLIMIT_DATA"), LINUX or FREEBSD) - ae(hasattr(psutil, "RLIMIT_FSIZE"), LINUX or FREEBSD) - ae(hasattr(psutil, "RLIMIT_MEMLOCK"), LINUX or FREEBSD) - ae(hasattr(psutil, "RLIMIT_NOFILE"), LINUX or FREEBSD) - ae(hasattr(psutil, "RLIMIT_NPROC"), LINUX or FREEBSD) - ae(hasattr(psutil, "RLIMIT_RSS"), LINUX or FREEBSD) - ae(hasattr(psutil, "RLIMIT_STACK"), LINUX or FREEBSD) - - ae(hasattr(psutil, "RLIMIT_LOCKS"), LINUX) - if POSIX: - if kernel_version() >= (2, 6, 8): - ae(hasattr(psutil, "RLIMIT_MSGQUEUE"), LINUX) - if kernel_version() >= (2, 6, 12): - ae(hasattr(psutil, "RLIMIT_NICE"), LINUX) - if kernel_version() >= (2, 6, 12): - ae(hasattr(psutil, "RLIMIT_RTPRIO"), LINUX) - if kernel_version() >= (2, 6, 25): - ae(hasattr(psutil, "RLIMIT_RTTIME"), LINUX) - if kernel_version() >= (2, 6, 8): - ae(hasattr(psutil, "RLIMIT_SIGPENDING"), LINUX) - - ae(hasattr(psutil, "RLIMIT_SWAP"), FREEBSD) - ae(hasattr(psutil, "RLIMIT_SBSIZE"), FREEBSD) - ae(hasattr(psutil, "RLIMIT_NPTS"), FREEBSD) - - -class TestAvailSystemAPIs(PsutilTestCase): - def test_win_service_iter(self): - assert hasattr(psutil, "win_service_iter") == WINDOWS - - def test_win_service_get(self): - assert hasattr(psutil, "win_service_get") == WINDOWS - - def test_cpu_freq(self): - assert hasattr(psutil, "cpu_freq") == ( - LINUX or MACOS or WINDOWS or FREEBSD or OPENBSD - ) - - def test_sensors_temperatures(self): - assert hasattr(psutil, "sensors_temperatures") == (LINUX or FREEBSD) - - def test_sensors_fans(self): - assert hasattr(psutil, "sensors_fans") == LINUX - - def test_battery(self): - assert hasattr(psutil, "sensors_battery") == ( - LINUX or WINDOWS or FREEBSD or MACOS - ) - - -class TestAvailProcessAPIs(PsutilTestCase): - def test_environ(self): - assert hasattr(psutil.Process, "environ") == ( - LINUX - or MACOS - or WINDOWS - or AIX - or SUNOS - or FREEBSD - or OPENBSD - or NETBSD - ) - - def test_uids(self): - assert hasattr(psutil.Process, "uids") == POSIX - - def test_gids(self): - assert hasattr(psutil.Process, "uids") == POSIX - - def test_terminal(self): - assert hasattr(psutil.Process, "terminal") == POSIX - - def test_ionice(self): - assert hasattr(psutil.Process, "ionice") == (LINUX or WINDOWS) - - @pytest.mark.skipif( - GITHUB_ACTIONS and LINUX, - reason="unsupported on GITHUB_ACTIONS + LINUX", - ) - def test_rlimit(self): - assert hasattr(psutil.Process, "rlimit") == (LINUX or FREEBSD) - - def test_io_counters(self): - hasit = hasattr(psutil.Process, "io_counters") - assert hasit == (not (MACOS or SUNOS)) - - def test_num_fds(self): - assert hasattr(psutil.Process, "num_fds") == POSIX - - def test_num_handles(self): - assert hasattr(psutil.Process, "num_handles") == WINDOWS - - def test_cpu_affinity(self): - assert hasattr(psutil.Process, "cpu_affinity") == ( - LINUX or WINDOWS or FREEBSD - ) - - def test_cpu_num(self): - assert hasattr(psutil.Process, "cpu_num") == ( - LINUX or FREEBSD or SUNOS - ) - - def test_memory_maps(self): - hasit = hasattr(psutil.Process, "memory_maps") - assert hasit == (not (OPENBSD or NETBSD or AIX or MACOS)) - - -# =================================================================== -# --- API types -# =================================================================== - - -class TestSystemAPITypes(PsutilTestCase): - """Check the return types of system related APIs. - https://github.com/giampaolo/psutil/issues/1039. - """ - - @classmethod - def setUpClass(cls): - cls.proc = psutil.Process() - - def assert_ntuple_of_nums(self, nt, type_=float, gezero=True): - assert is_namedtuple(nt) - for n in nt: - assert isinstance(n, type_) - if gezero: - assert n >= 0 - - def test_cpu_times(self): - self.assert_ntuple_of_nums(psutil.cpu_times()) - for nt in psutil.cpu_times(percpu=True): - self.assert_ntuple_of_nums(nt) - - def test_cpu_percent(self): - assert isinstance(psutil.cpu_percent(interval=None), float) - assert isinstance(psutil.cpu_percent(interval=0.00001), float) - - def test_cpu_times_percent(self): - self.assert_ntuple_of_nums(psutil.cpu_times_percent(interval=None)) - self.assert_ntuple_of_nums(psutil.cpu_times_percent(interval=0.0001)) - - def test_cpu_count(self): - assert isinstance(psutil.cpu_count(), int) - - # TODO: remove this once 1892 is fixed - @pytest.mark.skipif( - MACOS and platform.machine() == 'arm64', reason="skipped due to #1892" - ) - @pytest.mark.skipif(not HAS_CPU_FREQ, reason="not supported") - def test_cpu_freq(self): - if psutil.cpu_freq() is None: - raise pytest.skip("cpu_freq() returns None") - self.assert_ntuple_of_nums(psutil.cpu_freq(), type_=(float, int)) - - def test_disk_io_counters(self): - # Duplicate of test_system.py. Keep it anyway. - for k, v in psutil.disk_io_counters(perdisk=True).items(): - assert isinstance(k, str) - self.assert_ntuple_of_nums(v, type_=int) - - def test_disk_partitions(self): - # Duplicate of test_system.py. Keep it anyway. - for disk in psutil.disk_partitions(): - assert isinstance(disk.device, str) - assert isinstance(disk.mountpoint, str) - assert isinstance(disk.fstype, str) - assert isinstance(disk.opts, str) - - @pytest.mark.skipif(SKIP_SYSCONS, reason="requires root") - def test_net_connections(self): - with create_sockets(): - ret = psutil.net_connections('all') - assert len(ret) == len(set(ret)) - for conn in ret: - assert is_namedtuple(conn) - - def test_net_if_addrs(self): - # Duplicate of test_system.py. Keep it anyway. - for ifname, addrs in psutil.net_if_addrs().items(): - assert isinstance(ifname, str) - for addr in addrs: - assert isinstance(addr.family, enum.IntEnum) - assert isinstance(addr.address, str) - assert isinstance(addr.netmask, (str, type(None))) - assert isinstance(addr.broadcast, (str, type(None))) - - def test_net_if_stats(self): - # Duplicate of test_system.py. Keep it anyway. - for ifname, info in psutil.net_if_stats().items(): - assert isinstance(ifname, str) - assert isinstance(info.isup, bool) - assert isinstance(info.duplex, enum.IntEnum) - assert isinstance(info.speed, int) - assert isinstance(info.mtu, int) - - @pytest.mark.skipif(not HAS_NET_IO_COUNTERS, reason="not supported") - def test_net_io_counters(self): - # Duplicate of test_system.py. Keep it anyway. - for ifname in psutil.net_io_counters(pernic=True): - assert isinstance(ifname, str) - - @pytest.mark.skipif(not HAS_SENSORS_FANS, reason="not supported") - def test_sensors_fans(self): - # Duplicate of test_system.py. Keep it anyway. - for name, units in psutil.sensors_fans().items(): - assert isinstance(name, str) - for unit in units: - assert isinstance(unit.label, str) - assert isinstance(unit.current, (float, int, type(None))) - - @pytest.mark.skipif(not HAS_SENSORS_TEMPERATURES, reason="not supported") - def test_sensors_temperatures(self): - # Duplicate of test_system.py. Keep it anyway. - for name, units in psutil.sensors_temperatures().items(): - assert isinstance(name, str) - for unit in units: - assert isinstance(unit.label, str) - assert isinstance(unit.current, (float, int, type(None))) - assert isinstance(unit.high, (float, int, type(None))) - assert isinstance(unit.critical, (float, int, type(None))) - - def test_boot_time(self): - # Duplicate of test_system.py. Keep it anyway. - assert isinstance(psutil.boot_time(), float) - - def test_users(self): - # Duplicate of test_system.py. Keep it anyway. - for user in psutil.users(): - assert isinstance(user.name, str) - assert isinstance(user.terminal, (str, type(None))) - assert isinstance(user.host, (str, type(None))) - assert isinstance(user.pid, (int, type(None))) - - -class TestProcessWaitType(PsutilTestCase): - @pytest.mark.skipif(not POSIX, reason="not POSIX") - def test_negative_signal(self): - p = psutil.Process(self.spawn_testproc().pid) - p.terminate() - code = p.wait() - assert code == -signal.SIGTERM - assert isinstance(code, enum.IntEnum) diff --git a/PortablePython/Lib/site-packages/psutil/tests/test_linux.py b/PortablePython/Lib/site-packages/psutil/tests/test_linux.py deleted file mode 100644 index f4342d7..0000000 --- a/PortablePython/Lib/site-packages/psutil/tests/test_linux.py +++ /dev/null @@ -1,2292 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -"""Linux specific tests.""" - - -import collections -import contextlib -import errno -import io -import os -import platform -import re -import shutil -import socket -import struct -import textwrap -import time -import warnings -from unittest import mock - -import psutil -from psutil import LINUX -from psutil.tests import AARCH64 -from psutil.tests import GITHUB_ACTIONS -from psutil.tests import GLOBAL_TIMEOUT -from psutil.tests import HAS_BATTERY -from psutil.tests import HAS_CPU_FREQ -from psutil.tests import HAS_GETLOADAVG -from psutil.tests import HAS_RLIMIT -from psutil.tests import PYPY -from psutil.tests import PYTEST_PARALLEL -from psutil.tests import TOLERANCE_DISK_USAGE -from psutil.tests import TOLERANCE_SYS_MEM -from psutil.tests import PsutilTestCase -from psutil.tests import ThreadTask -from psutil.tests import call_until -from psutil.tests import pytest -from psutil.tests import reload_module -from psutil.tests import retry_on_failure -from psutil.tests import safe_rmpath -from psutil.tests import sh -from psutil.tests import skip_on_not_implemented - - -if LINUX: - from psutil._pslinux import CLOCK_TICKS - from psutil._pslinux import RootFsDeviceFinder - from psutil._pslinux import calculate_avail_vmem - from psutil._pslinux import open_binary - - -HERE = os.path.abspath(os.path.dirname(__file__)) -SIOCGIFADDR = 0x8915 -SIOCGIFHWADDR = 0x8927 -SIOCGIFNETMASK = 0x891B -SIOCGIFBRDADDR = 0x8919 -if LINUX: - SECTOR_SIZE = 512 -# ===================================================================== -# --- utils -# ===================================================================== - - -def get_ipv4_address(ifname): - import fcntl - - ifname = bytes(ifname[:15], "ascii") - with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s: - return socket.inet_ntoa( - fcntl.ioctl(s.fileno(), SIOCGIFADDR, struct.pack('256s', ifname))[ - 20:24 - ] - ) - - -def get_ipv4_netmask(ifname): - import fcntl - - ifname = bytes(ifname[:15], "ascii") - with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s: - return socket.inet_ntoa( - fcntl.ioctl( - s.fileno(), SIOCGIFNETMASK, struct.pack('256s', ifname) - )[20:24] - ) - - -def get_ipv4_broadcast(ifname): - import fcntl - - ifname = bytes(ifname[:15], "ascii") - with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s: - return socket.inet_ntoa( - fcntl.ioctl( - s.fileno(), SIOCGIFBRDADDR, struct.pack('256s', ifname) - )[20:24] - ) - - -def get_ipv6_addresses(ifname): - with open("/proc/net/if_inet6") as f: - all_fields = [] - for line in f: - fields = line.split() - if fields[-1] == ifname: - all_fields.append(fields) - - if len(all_fields) == 0: - raise ValueError(f"could not find interface {ifname!r}") - - for i in range(len(all_fields)): - unformatted = all_fields[i][0] - groups = [ - unformatted[j : j + 4] for j in range(0, len(unformatted), 4) - ] - formatted = ":".join(groups) - packed = socket.inet_pton(socket.AF_INET6, formatted) - all_fields[i] = socket.inet_ntop(socket.AF_INET6, packed) - return all_fields - - -def get_mac_address(ifname): - import fcntl - - ifname = bytes(ifname[:15], "ascii") - with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s: - info = fcntl.ioctl( - s.fileno(), SIOCGIFHWADDR, struct.pack('256s', ifname) - ) - return "".join([f"{char:02x}:" for char in info[18:24]])[:-1] - - -def free_swap(): - """Parse 'free' cmd and return swap memory's s total, used and free - values. - """ - out = sh(["free", "-b"], env={"LANG": "C.UTF-8"}) - lines = out.split('\n') - for line in lines: - if line.startswith('Swap'): - _, total, used, free = line.split() - nt = collections.namedtuple('free', 'total used free') - return nt(int(total), int(used), int(free)) - raise ValueError(f"can't find 'Swap' in 'free' output:\n{out}") - - -def free_physmem(): - """Parse 'free' cmd and return physical memory's total, used - and free values. - """ - # Note: free can have 2 different formats, invalidating 'shared' - # and 'cached' memory which may have different positions so we - # do not return them. - # https://github.com/giampaolo/psutil/issues/538#issuecomment-57059946 - out = sh(["free", "-b"], env={"LANG": "C.UTF-8"}) - lines = out.split('\n') - for line in lines: - if line.startswith('Mem'): - total, used, free, shared = (int(x) for x in line.split()[1:5]) - nt = collections.namedtuple( - 'free', 'total used free shared output' - ) - return nt(total, used, free, shared, out) - raise ValueError(f"can't find 'Mem' in 'free' output:\n{out}") - - -def vmstat(stat): - out = sh(["vmstat", "-s"], env={"LANG": "C.UTF-8"}) - for line in out.split("\n"): - line = line.strip() - if stat in line: - return int(line.split(' ')[0]) - raise ValueError(f"can't find {stat!r} in 'vmstat' output") - - -def get_free_version_info(): - out = sh(["free", "-V"]).strip() - if 'UNKNOWN' in out: - raise pytest.skip("can't determine free version") - return tuple(map(int, re.findall(r'\d+', out.split()[-1]))) - - -@contextlib.contextmanager -def mock_open_content(pairs): - """Mock open() builtin and forces it to return a certain content - for a given path. `pairs` is a {"path": "content", ...} dict. - """ - - def open_mock(name, *args, **kwargs): - if name in pairs: - content = pairs[name] - if isinstance(content, str): - return io.StringIO(content) - else: - return io.BytesIO(content) - else: - return orig_open(name, *args, **kwargs) - - orig_open = open - with mock.patch("builtins.open", create=True, side_effect=open_mock) as m: - yield m - - -@contextlib.contextmanager -def mock_open_exception(for_path, exc): - """Mock open() builtin and raises `exc` if the path being opened - matches `for_path`. - """ - - def open_mock(name, *args, **kwargs): - if name == for_path: - raise exc - return orig_open(name, *args, **kwargs) - - orig_open = open - with mock.patch("builtins.open", create=True, side_effect=open_mock) as m: - yield m - - -# ===================================================================== -# --- system virtual memory -# ===================================================================== - - -@pytest.mark.skipif(not LINUX, reason="LINUX only") -class TestSystemVirtualMemoryAgainstFree(PsutilTestCase): - def test_total(self): - cli_value = free_physmem().total - psutil_value = psutil.virtual_memory().total - assert cli_value == psutil_value - - @retry_on_failure() - def test_used(self): - # Older versions of procps used slab memory to calculate used memory. - # This got changed in: - # https://gitlab.com/procps-ng/procps/commit/ - # 05d751c4f076a2f0118b914c5e51cfbb4762ad8e - # Newer versions of procps are using yet another way to compute used - # memory. - # https://gitlab.com/procps-ng/procps/commit/ - # 2184e90d2e7cdb582f9a5b706b47015e56707e4d - if get_free_version_info() < (3, 3, 12): - raise pytest.skip("free version too old") - if get_free_version_info() >= (4, 0, 0): - raise pytest.skip("free version too recent") - cli_value = free_physmem().used - psutil_value = psutil.virtual_memory().used - assert abs(cli_value - psutil_value) < TOLERANCE_SYS_MEM - - @retry_on_failure() - def test_free(self): - cli_value = free_physmem().free - psutil_value = psutil.virtual_memory().free - assert abs(cli_value - psutil_value) < TOLERANCE_SYS_MEM - - @retry_on_failure() - def test_shared(self): - free = free_physmem() - free_value = free.shared - if free_value == 0: - raise pytest.skip("free does not support 'shared' column") - psutil_value = psutil.virtual_memory().shared - assert ( - abs(free_value - psutil_value) < TOLERANCE_SYS_MEM - ), f"{free_value} {psutil_value} \n{free.output}" - - @retry_on_failure() - def test_available(self): - # "free" output format has changed at some point: - # https://github.com/giampaolo/psutil/issues/538#issuecomment-147192098 - out = sh(["free", "-b"]) - lines = out.split('\n') - if 'available' not in lines[0]: - raise pytest.skip("free does not support 'available' column") - free_value = int(lines[1].split()[-1]) - psutil_value = psutil.virtual_memory().available - assert abs(free_value - psutil_value) < TOLERANCE_SYS_MEM - - -@pytest.mark.skipif(not LINUX, reason="LINUX only") -class TestSystemVirtualMemoryAgainstVmstat(PsutilTestCase): - def test_total(self): - vmstat_value = vmstat('total memory') * 1024 - psutil_value = psutil.virtual_memory().total - assert abs(vmstat_value - psutil_value) < TOLERANCE_SYS_MEM - - @retry_on_failure() - def test_used(self): - # Older versions of procps used slab memory to calculate used memory. - # This got changed in: - # https://gitlab.com/procps-ng/procps/commit/ - # 05d751c4f076a2f0118b914c5e51cfbb4762ad8e - # Newer versions of procps are using yet another way to compute used - # memory. - # https://gitlab.com/procps-ng/procps/commit/ - # 2184e90d2e7cdb582f9a5b706b47015e56707e4d - if get_free_version_info() < (3, 3, 12): - raise pytest.skip("free version too old") - if get_free_version_info() >= (4, 0, 0): - raise pytest.skip("free version too recent") - vmstat_value = vmstat('used memory') * 1024 - psutil_value = psutil.virtual_memory().used - assert abs(vmstat_value - psutil_value) < TOLERANCE_SYS_MEM - - @retry_on_failure() - def test_free(self): - vmstat_value = vmstat('free memory') * 1024 - psutil_value = psutil.virtual_memory().free - assert abs(vmstat_value - psutil_value) < TOLERANCE_SYS_MEM - - @retry_on_failure() - def test_buffers(self): - vmstat_value = vmstat('buffer memory') * 1024 - psutil_value = psutil.virtual_memory().buffers - assert abs(vmstat_value - psutil_value) < TOLERANCE_SYS_MEM - - @retry_on_failure() - def test_active(self): - vmstat_value = vmstat('active memory') * 1024 - psutil_value = psutil.virtual_memory().active - assert abs(vmstat_value - psutil_value) < TOLERANCE_SYS_MEM - - @retry_on_failure() - def test_inactive(self): - vmstat_value = vmstat('inactive memory') * 1024 - psutil_value = psutil.virtual_memory().inactive - assert abs(vmstat_value - psutil_value) < TOLERANCE_SYS_MEM - - -@pytest.mark.skipif(not LINUX, reason="LINUX only") -class TestSystemVirtualMemoryMocks(PsutilTestCase): - def test_warnings_on_misses(self): - # Emulate a case where /proc/meminfo provides few info. - # psutil is supposed to set the missing fields to 0 and - # raise a warning. - content = textwrap.dedent("""\ - Active(anon): 6145416 kB - Active(file): 2950064 kB - Inactive(anon): 574764 kB - Inactive(file): 1567648 kB - MemAvailable: -1 kB - MemFree: 2057400 kB - MemTotal: 16325648 kB - SReclaimable: 346648 kB - """).encode() - with mock_open_content({'/proc/meminfo': content}) as m: - with warnings.catch_warnings(record=True) as ws: - warnings.simplefilter("always") - ret = psutil.virtual_memory() - assert m.called - assert len(ws) == 1 - w = ws[0] - assert "memory stats couldn't be determined" in str(w.message) - assert "cached" in str(w.message) - assert "shared" in str(w.message) - assert "active" in str(w.message) - assert "inactive" in str(w.message) - assert "buffers" in str(w.message) - assert "available" in str(w.message) - assert ret.cached == 0 - assert ret.active == 0 - assert ret.inactive == 0 - assert ret.shared == 0 - assert ret.buffers == 0 - assert ret.available == 0 - assert ret.slab == 0 - - @retry_on_failure() - def test_avail_old_percent(self): - # Make sure that our calculation of avail mem for old kernels - # is off by max 15%. - mems = {} - with open_binary('/proc/meminfo') as f: - for line in f: - fields = line.split() - mems[fields[0]] = int(fields[1]) * 1024 - - a = calculate_avail_vmem(mems) - if b'MemAvailable:' in mems: - b = mems[b'MemAvailable:'] - diff_percent = abs(a - b) / a * 100 - assert diff_percent < 15 - - def test_avail_old_comes_from_kernel(self): - # Make sure "MemAvailable:" coluimn is used instead of relying - # on our internal algorithm to calculate avail mem. - content = textwrap.dedent("""\ - Active: 9444728 kB - Active(anon): 6145416 kB - Active(file): 2950064 kB - Buffers: 287952 kB - Cached: 4818144 kB - Inactive(file): 1578132 kB - Inactive(anon): 574764 kB - Inactive(file): 1567648 kB - MemAvailable: 6574984 kB - MemFree: 2057400 kB - MemTotal: 16325648 kB - Shmem: 577588 kB - SReclaimable: 346648 kB - """).encode() - with mock_open_content({'/proc/meminfo': content}) as m: - with warnings.catch_warnings(record=True) as ws: - ret = psutil.virtual_memory() - assert m.called - assert ret.available == 6574984 * 1024 - w = ws[0] - assert "inactive memory stats couldn't be determined" in str( - w.message - ) - - def test_avail_old_missing_fields(self): - # Remove Active(file), Inactive(file) and SReclaimable - # from /proc/meminfo and make sure the fallback is used - # (free + cached), - content = textwrap.dedent("""\ - Active: 9444728 kB - Active(anon): 6145416 kB - Buffers: 287952 kB - Cached: 4818144 kB - Inactive(file): 1578132 kB - Inactive(anon): 574764 kB - MemFree: 2057400 kB - MemTotal: 16325648 kB - Shmem: 577588 kB - """).encode() - with mock_open_content({"/proc/meminfo": content}) as m: - with warnings.catch_warnings(record=True) as ws: - ret = psutil.virtual_memory() - assert m.called - assert ret.available == 2057400 * 1024 + 4818144 * 1024 - w = ws[0] - assert "inactive memory stats couldn't be determined" in str( - w.message - ) - - def test_avail_old_missing_zoneinfo(self): - # Remove /proc/zoneinfo file. Make sure fallback is used - # (free + cached). - content = textwrap.dedent("""\ - Active: 9444728 kB - Active(anon): 6145416 kB - Active(file): 2950064 kB - Buffers: 287952 kB - Cached: 4818144 kB - Inactive(file): 1578132 kB - Inactive(anon): 574764 kB - Inactive(file): 1567648 kB - MemFree: 2057400 kB - MemTotal: 16325648 kB - Shmem: 577588 kB - SReclaimable: 346648 kB - """).encode() - with mock_open_content({"/proc/meminfo": content}): - with mock_open_exception("/proc/zoneinfo", FileNotFoundError): - with warnings.catch_warnings(record=True) as ws: - ret = psutil.virtual_memory() - assert ret.available == 2057400 * 1024 + 4818144 * 1024 - w = ws[0] - assert ( - "inactive memory stats couldn't be determined" - in str(w.message) - ) - - def test_virtual_memory_mocked(self): - # Emulate /proc/meminfo because neither vmstat nor free return slab. - content = textwrap.dedent("""\ - MemTotal: 100 kB - MemFree: 2 kB - MemAvailable: 3 kB - Buffers: 4 kB - Cached: 5 kB - SwapCached: 6 kB - Active: 7 kB - Inactive: 8 kB - Active(anon): 9 kB - Inactive(anon): 10 kB - Active(file): 11 kB - Inactive(file): 12 kB - Unevictable: 13 kB - Mlocked: 14 kB - SwapTotal: 15 kB - SwapFree: 16 kB - Dirty: 17 kB - Writeback: 18 kB - AnonPages: 19 kB - Mapped: 20 kB - Shmem: 21 kB - Slab: 22 kB - SReclaimable: 23 kB - SUnreclaim: 24 kB - KernelStack: 25 kB - PageTables: 26 kB - NFS_Unstable: 27 kB - Bounce: 28 kB - WritebackTmp: 29 kB - CommitLimit: 30 kB - Committed_AS: 31 kB - VmallocTotal: 32 kB - VmallocUsed: 33 kB - VmallocChunk: 34 kB - HardwareCorrupted: 35 kB - AnonHugePages: 36 kB - ShmemHugePages: 37 kB - ShmemPmdMapped: 38 kB - CmaTotal: 39 kB - CmaFree: 40 kB - HugePages_Total: 41 kB - HugePages_Free: 42 kB - HugePages_Rsvd: 43 kB - HugePages_Surp: 44 kB - Hugepagesize: 45 kB - DirectMap46k: 46 kB - DirectMap47M: 47 kB - DirectMap48G: 48 kB - """).encode() - with mock_open_content({"/proc/meminfo": content}) as m: - mem = psutil.virtual_memory() - assert m.called - assert mem.total == 100 * 1024 - assert mem.free == 2 * 1024 - assert mem.buffers == 4 * 1024 - # cached mem also includes reclaimable memory - assert mem.cached == (5 + 23) * 1024 - assert mem.shared == 21 * 1024 - assert mem.active == 7 * 1024 - assert mem.inactive == 8 * 1024 - assert mem.slab == 22 * 1024 - assert mem.available == 3 * 1024 - - -# ===================================================================== -# --- system swap memory -# ===================================================================== - - -@pytest.mark.skipif(not LINUX, reason="LINUX only") -class TestSystemSwapMemory(PsutilTestCase): - @staticmethod - def meminfo_has_swap_info(): - """Return True if /proc/meminfo provides swap metrics.""" - with open("/proc/meminfo") as f: - data = f.read() - return 'SwapTotal:' in data and 'SwapFree:' in data - - def test_total(self): - free_value = free_swap().total - psutil_value = psutil.swap_memory().total - assert abs(free_value - psutil_value) < TOLERANCE_SYS_MEM - - @retry_on_failure() - def test_used(self): - free_value = free_swap().used - psutil_value = psutil.swap_memory().used - assert abs(free_value - psutil_value) < TOLERANCE_SYS_MEM - - @retry_on_failure() - def test_free(self): - free_value = free_swap().free - psutil_value = psutil.swap_memory().free - assert abs(free_value - psutil_value) < TOLERANCE_SYS_MEM - - def test_missing_sin_sout(self): - with mock.patch('psutil._common.open', create=True) as m: - with warnings.catch_warnings(record=True) as ws: - warnings.simplefilter("always") - ret = psutil.swap_memory() - assert m.called - assert len(ws) == 1 - w = ws[0] - assert ( - "'sin' and 'sout' swap memory stats couldn't be determined" - in str(w.message) - ) - assert ret.sin == 0 - assert ret.sout == 0 - - def test_no_vmstat_mocked(self): - # see https://github.com/giampaolo/psutil/issues/722 - with mock_open_exception("/proc/vmstat", FileNotFoundError) as m: - with warnings.catch_warnings(record=True) as ws: - warnings.simplefilter("always") - ret = psutil.swap_memory() - assert m.called - assert len(ws) == 1 - w = ws[0] - assert ( - "'sin' and 'sout' swap memory stats couldn't " - "be determined and were set to 0" - in str(w.message) - ) - assert ret.sin == 0 - assert ret.sout == 0 - - def test_meminfo_against_sysinfo(self): - # Make sure the content of /proc/meminfo about swap memory - # matches sysinfo() syscall, see: - # https://github.com/giampaolo/psutil/issues/1015 - if not self.meminfo_has_swap_info(): - raise pytest.skip("/proc/meminfo has no swap metrics") - with mock.patch('psutil._pslinux.cext.linux_sysinfo') as m: - swap = psutil.swap_memory() - assert not m.called - import psutil._psutil_linux as cext - - _, _, _, _, total, free, unit_multiplier = cext.linux_sysinfo() - total *= unit_multiplier - free *= unit_multiplier - assert swap.total == total - assert abs(swap.free - free) < TOLERANCE_SYS_MEM - - def test_emulate_meminfo_has_no_metrics(self): - # Emulate a case where /proc/meminfo provides no swap metrics - # in which case sysinfo() syscall is supposed to be used - # as a fallback. - with mock_open_content({"/proc/meminfo": b""}) as m: - psutil.swap_memory() - assert m.called - - -# ===================================================================== -# --- system CPU -# ===================================================================== - - -@pytest.mark.skipif(not LINUX, reason="LINUX only") -class TestSystemCPUTimes(PsutilTestCase): - def test_fields(self): - fields = psutil.cpu_times()._fields - kernel_ver = re.findall(r'\d+\.\d+\.\d+', os.uname()[2])[0] - kernel_ver_info = tuple(map(int, kernel_ver.split('.'))) - if kernel_ver_info >= (2, 6, 11): - assert 'steal' in fields - else: - assert 'steal' not in fields - if kernel_ver_info >= (2, 6, 24): - assert 'guest' in fields - else: - assert 'guest' not in fields - if kernel_ver_info >= (3, 2, 0): - assert 'guest_nice' in fields - else: - assert 'guest_nice' not in fields - - -@pytest.mark.skipif(not LINUX, reason="LINUX only") -class TestSystemCPUCountLogical(PsutilTestCase): - @pytest.mark.skipif( - not os.path.exists("/sys/devices/system/cpu/online"), - reason="/sys/devices/system/cpu/online does not exist", - ) - def test_against_sysdev_cpu_online(self): - with open("/sys/devices/system/cpu/online") as f: - value = f.read().strip() - if "-" in str(value): - value = int(value.split('-')[1]) + 1 - assert psutil.cpu_count() == value - - @pytest.mark.skipif( - not os.path.exists("/sys/devices/system/cpu"), - reason="/sys/devices/system/cpu does not exist", - ) - def test_against_sysdev_cpu_num(self): - ls = os.listdir("/sys/devices/system/cpu") - count = len([x for x in ls if re.search(r"cpu\d+$", x) is not None]) - assert psutil.cpu_count() == count - - @pytest.mark.skipif( - not shutil.which("nproc"), reason="nproc utility not available" - ) - def test_against_nproc(self): - num = int(sh("nproc --all")) - assert psutil.cpu_count(logical=True) == num - - @pytest.mark.skipif( - not shutil.which("lscpu"), reason="lscpu utility not available" - ) - def test_against_lscpu(self): - out = sh("lscpu -p") - num = len([x for x in out.split('\n') if not x.startswith('#')]) - assert psutil.cpu_count(logical=True) == num - - def test_emulate_fallbacks(self): - import psutil._pslinux - - original = psutil._pslinux.cpu_count_logical() - # Here we want to mock os.sysconf("SC_NPROCESSORS_ONLN") in - # order to cause the parsing of /proc/cpuinfo and /proc/stat. - with mock.patch( - 'psutil._pslinux.os.sysconf', side_effect=ValueError - ) as m: - assert psutil._pslinux.cpu_count_logical() == original - assert m.called - - # Let's have open() return empty data and make sure None is - # returned ('cause we mimic os.cpu_count()). - with mock.patch('psutil._common.open', create=True) as m: - assert psutil._pslinux.cpu_count_logical() is None - assert m.call_count == 2 - # /proc/stat should be the last one - assert m.call_args[0][0] == '/proc/stat' - - # Let's push this a bit further and make sure /proc/cpuinfo - # parsing works as expected. - with open('/proc/cpuinfo', 'rb') as f: - cpuinfo_data = f.read() - fake_file = io.BytesIO(cpuinfo_data) - with mock.patch( - 'psutil._common.open', return_value=fake_file, create=True - ) as m: - assert psutil._pslinux.cpu_count_logical() == original - - # Finally, let's make /proc/cpuinfo return meaningless data; - # this way we'll fall back on relying on /proc/stat - with mock_open_content({"/proc/cpuinfo": b""}) as m: - assert psutil._pslinux.cpu_count_logical() == original - assert m.called - - -@pytest.mark.skipif(not LINUX, reason="LINUX only") -class TestSystemCPUCountCores(PsutilTestCase): - @pytest.mark.skipif( - not shutil.which("lscpu"), reason="lscpu utility not available" - ) - def test_against_lscpu(self): - out = sh("lscpu -p") - core_ids = set() - for line in out.split('\n'): - if not line.startswith('#'): - fields = line.split(',') - core_ids.add(fields[1]) - assert psutil.cpu_count(logical=False) == len(core_ids) - - @pytest.mark.skipif( - platform.machine() not in {"x86_64", "i686"}, reason="x86_64/i686 only" - ) - def test_method_2(self): - meth_1 = psutil._pslinux.cpu_count_cores() - with mock.patch('glob.glob', return_value=[]) as m: - meth_2 = psutil._pslinux.cpu_count_cores() - assert m.called - if meth_1 is not None: - assert meth_1 == meth_2 - - def test_emulate_none(self): - with mock.patch('glob.glob', return_value=[]) as m1: - with mock.patch('psutil._common.open', create=True) as m2: - assert psutil._pslinux.cpu_count_cores() is None - assert m1.called - assert m2.called - - -@pytest.mark.skipif(not LINUX, reason="LINUX only") -class TestSystemCPUFrequency(PsutilTestCase): - @pytest.mark.skipif(not HAS_CPU_FREQ, reason="not supported") - @pytest.mark.skipif( - AARCH64, reason="aarch64 does not always expose frequency" - ) - def test_emulate_use_second_file(self): - # https://github.com/giampaolo/psutil/issues/981 - def path_exists_mock(path): - if path.startswith("/sys/devices/system/cpu/cpufreq/policy"): - return False - else: - return orig_exists(path) - - orig_exists = os.path.exists - with mock.patch( - "os.path.exists", side_effect=path_exists_mock, create=True - ): - assert psutil.cpu_freq() - - @pytest.mark.skipif(not HAS_CPU_FREQ, reason="not supported") - @pytest.mark.skipif( - AARCH64, reason="aarch64 does not report mhz in /proc/cpuinfo" - ) - def test_emulate_use_cpuinfo(self): - # Emulate a case where /sys/devices/system/cpu/cpufreq* does not - # exist and /proc/cpuinfo is used instead. - def path_exists_mock(path): - if path.startswith('/sys/devices/system/cpu/'): - return False - else: - return os_path_exists(path) - - os_path_exists = os.path.exists - try: - with mock.patch("os.path.exists", side_effect=path_exists_mock): - reload_module(psutil._pslinux) - ret = psutil.cpu_freq() - assert ret, ret - assert ret.max == 0.0 - assert ret.min == 0.0 - for freq in psutil.cpu_freq(percpu=True): - assert freq.max == 0.0 - assert freq.min == 0.0 - finally: - reload_module(psutil._pslinux) - reload_module(psutil) - - @pytest.mark.skipif(not HAS_CPU_FREQ, reason="not supported") - def test_emulate_data(self): - def open_mock(name, *args, **kwargs): - if name.endswith('/scaling_cur_freq') and name.startswith( - "/sys/devices/system/cpu/cpufreq/policy" - ): - return io.BytesIO(b"500000") - elif name.endswith('/scaling_min_freq') and name.startswith( - "/sys/devices/system/cpu/cpufreq/policy" - ): - return io.BytesIO(b"600000") - elif name.endswith('/scaling_max_freq') and name.startswith( - "/sys/devices/system/cpu/cpufreq/policy" - ): - return io.BytesIO(b"700000") - elif name == '/proc/cpuinfo': - return io.BytesIO(b"cpu MHz : 500") - else: - return orig_open(name, *args, **kwargs) - - orig_open = open - with mock.patch("builtins.open", side_effect=open_mock): - with mock.patch('os.path.exists', return_value=True): - freq = psutil.cpu_freq() - assert freq.current == 500.0 - # when /proc/cpuinfo is used min and max frequencies are not - # available and are set to 0. - if freq.min != 0.0: - assert freq.min == 600.0 - if freq.max != 0.0: - assert freq.max == 700.0 - - @pytest.mark.skipif(not HAS_CPU_FREQ, reason="not supported") - def test_emulate_multi_cpu(self): - def open_mock(name, *args, **kwargs): - n = name - if n.endswith('/scaling_cur_freq') and n.startswith( - "/sys/devices/system/cpu/cpufreq/policy0" - ): - return io.BytesIO(b"100000") - elif n.endswith('/scaling_min_freq') and n.startswith( - "/sys/devices/system/cpu/cpufreq/policy0" - ): - return io.BytesIO(b"200000") - elif n.endswith('/scaling_max_freq') and n.startswith( - "/sys/devices/system/cpu/cpufreq/policy0" - ): - return io.BytesIO(b"300000") - elif n.endswith('/scaling_cur_freq') and n.startswith( - "/sys/devices/system/cpu/cpufreq/policy1" - ): - return io.BytesIO(b"400000") - elif n.endswith('/scaling_min_freq') and n.startswith( - "/sys/devices/system/cpu/cpufreq/policy1" - ): - return io.BytesIO(b"500000") - elif n.endswith('/scaling_max_freq') and n.startswith( - "/sys/devices/system/cpu/cpufreq/policy1" - ): - return io.BytesIO(b"600000") - elif name == '/proc/cpuinfo': - return io.BytesIO(b"cpu MHz : 100\ncpu MHz : 400") - else: - return orig_open(name, *args, **kwargs) - - orig_open = open - with mock.patch("builtins.open", side_effect=open_mock): - with mock.patch('os.path.exists', return_value=True): - with mock.patch( - 'psutil._pslinux.cpu_count_logical', return_value=2 - ): - freq = psutil.cpu_freq(percpu=True) - assert freq[0].current == 100.0 - if freq[0].min != 0.0: - assert freq[0].min == 200.0 - if freq[0].max != 0.0: - assert freq[0].max == 300.0 - assert freq[1].current == 400.0 - if freq[1].min != 0.0: - assert freq[1].min == 500.0 - if freq[1].max != 0.0: - assert freq[1].max == 600.0 - - @pytest.mark.skipif(not HAS_CPU_FREQ, reason="not supported") - def test_emulate_no_scaling_cur_freq_file(self): - # See: https://github.com/giampaolo/psutil/issues/1071 - def open_mock(name, *args, **kwargs): - if name.endswith('/scaling_cur_freq'): - raise FileNotFoundError - if name.endswith('/cpuinfo_cur_freq'): - return io.BytesIO(b"200000") - elif name == '/proc/cpuinfo': - return io.BytesIO(b"cpu MHz : 200") - else: - return orig_open(name, *args, **kwargs) - - orig_open = open - with mock.patch("builtins.open", side_effect=open_mock): - with mock.patch('os.path.exists', return_value=True): - with mock.patch( - 'psutil._pslinux.cpu_count_logical', return_value=1 - ): - freq = psutil.cpu_freq() - assert freq.current == 200 - - -@pytest.mark.skipif(not LINUX, reason="LINUX only") -class TestSystemCPUStats(PsutilTestCase): - - # XXX: fails too often. - # def test_ctx_switches(self): - # vmstat_value = vmstat("context switches") - # psutil_value = psutil.cpu_stats().ctx_switches - # self.assertAlmostEqual(vmstat_value, psutil_value, delta=500) - - def test_interrupts(self): - vmstat_value = vmstat("interrupts") - psutil_value = psutil.cpu_stats().interrupts - assert abs(vmstat_value - psutil_value) < 500 - - -@pytest.mark.skipif(not LINUX, reason="LINUX only") -class TestLoadAvg(PsutilTestCase): - @pytest.mark.skipif(not HAS_GETLOADAVG, reason="not supported") - def test_getloadavg(self): - psutil_value = psutil.getloadavg() - with open("/proc/loadavg") as f: - proc_value = f.read().split() - - assert abs(float(proc_value[0]) - psutil_value[0]) < 1 - assert abs(float(proc_value[1]) - psutil_value[1]) < 1 - assert abs(float(proc_value[2]) - psutil_value[2]) < 1 - - -# ===================================================================== -# --- system network -# ===================================================================== - - -@pytest.mark.skipif(not LINUX, reason="LINUX only") -class TestSystemNetIfAddrs(PsutilTestCase): - def test_ips(self): - for name, addrs in psutil.net_if_addrs().items(): - for addr in addrs: - if addr.family == psutil.AF_LINK: - assert addr.address == get_mac_address(name) - elif addr.family == socket.AF_INET: - assert addr.address == get_ipv4_address(name) - assert addr.netmask == get_ipv4_netmask(name) - if addr.broadcast is not None: - assert addr.broadcast == get_ipv4_broadcast(name) - else: - assert get_ipv4_broadcast(name) == '0.0.0.0' - elif addr.family == socket.AF_INET6: - # IPv6 addresses can have a percent symbol at the end. - # E.g. these 2 are equivalent: - # "fe80::1ff:fe23:4567:890a" - # "fe80::1ff:fe23:4567:890a%eth0" - # That is the "zone id" portion, which usually is the name - # of the network interface. - address = addr.address.split('%')[0] - assert address in get_ipv6_addresses(name) - - # XXX - not reliable when having virtual NICs installed by Docker. - # @pytest.mark.skipif(not shutil.which("ip"), - # reason="'ip' utility not available") - # def test_net_if_names(self): - # out = sh("ip addr").strip() - # nics = [x for x in psutil.net_if_addrs().keys() if ':' not in x] - # found = 0 - # for line in out.split('\n'): - # line = line.strip() - # if re.search(r"^\d+:", line): - # found += 1 - # name = line.split(':')[1].strip() - # self.assertIn(name, nics) - # self.assertEqual(len(nics), found, msg="{}\n---\n{}".format( - # pprint.pformat(nics), out)) - - -@pytest.mark.skipif(not LINUX, reason="LINUX only") -class TestSystemNetIfStats(PsutilTestCase): - @pytest.mark.skipif( - not shutil.which("ifconfig"), reason="ifconfig utility not available" - ) - def test_against_ifconfig(self): - for name, stats in psutil.net_if_stats().items(): - try: - out = sh(f"ifconfig {name}") - except RuntimeError: - pass - else: - assert stats.isup == ('RUNNING' in out), out - assert stats.mtu == int( - re.findall(r'(?i)MTU[: ](\d+)', out)[0] - ) - - def test_mtu(self): - for name, stats in psutil.net_if_stats().items(): - with open(f"/sys/class/net/{name}/mtu") as f: - assert stats.mtu == int(f.read().strip()) - - @pytest.mark.skipif( - not shutil.which("ifconfig"), reason="ifconfig utility not available" - ) - def test_flags(self): - # first line looks like this: - # "eth0: flags=4163 mtu 1500" - matches_found = 0 - for name, stats in psutil.net_if_stats().items(): - try: - out = sh(f"ifconfig {name}") - except RuntimeError: - pass - else: - match = re.search(r"flags=(\d+)?<(.*?)>", out) - if match and len(match.groups()) >= 2: - matches_found += 1 - ifconfig_flags = set(match.group(2).lower().split(",")) - psutil_flags = set(stats.flags.split(",")) - assert ifconfig_flags == psutil_flags - else: - # ifconfig has a different output on CentOS 6 - # let's try that - match = re.search(r"(.*) MTU:(\d+) Metric:(\d+)", out) - if match and len(match.groups()) >= 3: - matches_found += 1 - ifconfig_flags = set(match.group(1).lower().split()) - psutil_flags = set(stats.flags.split(",")) - assert ifconfig_flags == psutil_flags - - if not matches_found: - raise self.fail("no matches were found") - - -@pytest.mark.skipif(not LINUX, reason="LINUX only") -class TestSystemNetIOCounters(PsutilTestCase): - @pytest.mark.skipif( - not shutil.which("ifconfig"), reason="ifconfig utility not available" - ) - @retry_on_failure() - def test_against_ifconfig(self): - def ifconfig(nic): - ret = {} - out = sh(f"ifconfig {nic}") - ret['packets_recv'] = int( - re.findall(r'RX packets[: ](\d+)', out)[0] - ) - ret['packets_sent'] = int( - re.findall(r'TX packets[: ](\d+)', out)[0] - ) - ret['errin'] = int(re.findall(r'errors[: ](\d+)', out)[0]) - ret['errout'] = int(re.findall(r'errors[: ](\d+)', out)[1]) - ret['dropin'] = int(re.findall(r'dropped[: ](\d+)', out)[0]) - ret['dropout'] = int(re.findall(r'dropped[: ](\d+)', out)[1]) - ret['bytes_recv'] = int( - re.findall(r'RX (?:packets \d+ +)?bytes[: ](\d+)', out)[0] - ) - ret['bytes_sent'] = int( - re.findall(r'TX (?:packets \d+ +)?bytes[: ](\d+)', out)[0] - ) - return ret - - nio = psutil.net_io_counters(pernic=True, nowrap=False) - for name, stats in nio.items(): - try: - ifconfig_ret = ifconfig(name) - except RuntimeError: - continue - assert ( - abs(stats.bytes_recv - ifconfig_ret['bytes_recv']) < 1024 * 10 - ) - assert ( - abs(stats.bytes_sent - ifconfig_ret['bytes_sent']) < 1024 * 10 - ) - assert ( - abs(stats.packets_recv - ifconfig_ret['packets_recv']) < 1024 - ) - assert ( - abs(stats.packets_sent - ifconfig_ret['packets_sent']) < 1024 - ) - assert abs(stats.errin - ifconfig_ret['errin']) < 10 - assert abs(stats.errout - ifconfig_ret['errout']) < 10 - assert abs(stats.dropin - ifconfig_ret['dropin']) < 10 - assert abs(stats.dropout - ifconfig_ret['dropout']) < 10 - - -@pytest.mark.skipif(not LINUX, reason="LINUX only") -class TestSystemNetConnections(PsutilTestCase): - @mock.patch('psutil._pslinux.socket.inet_ntop', side_effect=ValueError) - @mock.patch('psutil._pslinux.supports_ipv6', return_value=False) - def test_emulate_ipv6_unsupported(self, supports_ipv6, inet_ntop): - # see: https://github.com/giampaolo/psutil/issues/623 - try: - s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) - self.addCleanup(s.close) - s.bind(("::1", 0)) - except OSError: - pass - psutil.net_connections(kind='inet6') - - def test_emulate_unix(self): - content = textwrap.dedent("""\ - 0: 00000003 000 000 0001 03 462170 @/tmp/dbus-Qw2hMPIU3n - 0: 00000003 000 000 0001 03 35010 @/tmp/dbus-tB2X8h69BQ - 0: 00000003 000 000 0001 03 34424 @/tmp/dbus-cHy80Y8O - 000000000000000000000000000000000000000000000000000000 - """) - with mock_open_content({"/proc/net/unix": content}) as m: - psutil.net_connections(kind='unix') - assert m.called - - -# ===================================================================== -# --- system disks -# ===================================================================== - - -@pytest.mark.skipif(not LINUX, reason="LINUX only") -class TestSystemDiskPartitions(PsutilTestCase): - @pytest.mark.skipif( - not hasattr(os, 'statvfs'), reason="os.statvfs() not available" - ) - @skip_on_not_implemented() - def test_against_df(self): - # test psutil.disk_usage() and psutil.disk_partitions() - # against "df -a" - def df(path): - out = sh(f'df -P -B 1 "{path}"').strip() - lines = out.split('\n') - lines.pop(0) - line = lines.pop(0) - dev, total, used, free = line.split()[:4] - if dev == 'none': - dev = '' - total, used, free = int(total), int(used), int(free) - return dev, total, used, free - - for part in psutil.disk_partitions(all=False): - usage = psutil.disk_usage(part.mountpoint) - _, total, used, free = df(part.mountpoint) - assert usage.total == total - assert abs(usage.free - free) < TOLERANCE_DISK_USAGE - assert abs(usage.used - used) < TOLERANCE_DISK_USAGE - - def test_zfs_fs(self): - # Test that ZFS partitions are returned. - with open("/proc/filesystems") as f: - data = f.read() - if 'zfs' in data: - for part in psutil.disk_partitions(): - if part.fstype == 'zfs': - return - - # No ZFS partitions on this system. Let's fake one. - fake_file = io.StringIO("nodev\tzfs\n") - with mock.patch( - 'psutil._common.open', return_value=fake_file, create=True - ) as m1: - with mock.patch( - 'psutil._pslinux.cext.disk_partitions', - return_value=[('/dev/sdb3', '/', 'zfs', 'rw')], - ) as m2: - ret = psutil.disk_partitions() - assert m1.called - assert m2.called - assert ret - assert ret[0].fstype == 'zfs' - - def test_emulate_realpath_fail(self): - # See: https://github.com/giampaolo/psutil/issues/1307 - try: - with mock.patch( - 'os.path.realpath', return_value='/non/existent' - ) as m: - with pytest.raises(FileNotFoundError): - psutil.disk_partitions() - assert m.called - finally: - psutil.PROCFS_PATH = "/proc" - - -@pytest.mark.skipif(not LINUX, reason="LINUX only") -class TestSystemDiskIoCounters(PsutilTestCase): - def test_emulate_kernel_2_4(self): - # Tests /proc/diskstats parsing format for 2.4 kernels, see: - # https://github.com/giampaolo/psutil/issues/767 - content = " 3 0 1 hda 2 3 4 5 6 7 8 9 10 11 12" - with mock_open_content({'/proc/diskstats': content}): - with mock.patch( - 'psutil._pslinux.is_storage_device', return_value=True - ): - ret = psutil.disk_io_counters(nowrap=False) - assert ret.read_count == 1 - assert ret.read_merged_count == 2 - assert ret.read_bytes == 3 * SECTOR_SIZE - assert ret.read_time == 4 - assert ret.write_count == 5 - assert ret.write_merged_count == 6 - assert ret.write_bytes == 7 * SECTOR_SIZE - assert ret.write_time == 8 - assert ret.busy_time == 10 - - def test_emulate_kernel_2_6_full(self): - # Tests /proc/diskstats parsing format for 2.6 kernels, - # lines reporting all metrics: - # https://github.com/giampaolo/psutil/issues/767 - content = " 3 0 hda 1 2 3 4 5 6 7 8 9 10 11" - with mock_open_content({"/proc/diskstats": content}): - with mock.patch( - 'psutil._pslinux.is_storage_device', return_value=True - ): - ret = psutil.disk_io_counters(nowrap=False) - assert ret.read_count == 1 - assert ret.read_merged_count == 2 - assert ret.read_bytes == 3 * SECTOR_SIZE - assert ret.read_time == 4 - assert ret.write_count == 5 - assert ret.write_merged_count == 6 - assert ret.write_bytes == 7 * SECTOR_SIZE - assert ret.write_time == 8 - assert ret.busy_time == 10 - - def test_emulate_kernel_2_6_limited(self): - # Tests /proc/diskstats parsing format for 2.6 kernels, - # where one line of /proc/partitions return a limited - # amount of metrics when it bumps into a partition - # (instead of a disk). See: - # https://github.com/giampaolo/psutil/issues/767 - with mock_open_content({"/proc/diskstats": " 3 1 hda 1 2 3 4"}): - with mock.patch( - 'psutil._pslinux.is_storage_device', return_value=True - ): - ret = psutil.disk_io_counters(nowrap=False) - assert ret.read_count == 1 - assert ret.read_bytes == 2 * SECTOR_SIZE - assert ret.write_count == 3 - assert ret.write_bytes == 4 * SECTOR_SIZE - - assert ret.read_merged_count == 0 - assert ret.read_time == 0 - assert ret.write_merged_count == 0 - assert ret.write_time == 0 - assert ret.busy_time == 0 - - def test_emulate_include_partitions(self): - # Make sure that when perdisk=True disk partitions are returned, - # see: - # https://github.com/giampaolo/psutil/pull/1313#issuecomment-408626842 - content = textwrap.dedent("""\ - 3 0 nvme0n1 1 2 3 4 5 6 7 8 9 10 11 - 3 0 nvme0n1p1 1 2 3 4 5 6 7 8 9 10 11 - """) - with mock_open_content({"/proc/diskstats": content}): - with mock.patch( - 'psutil._pslinux.is_storage_device', return_value=False - ): - ret = psutil.disk_io_counters(perdisk=True, nowrap=False) - assert len(ret) == 2 - assert ret['nvme0n1'].read_count == 1 - assert ret['nvme0n1p1'].read_count == 1 - assert ret['nvme0n1'].write_count == 5 - assert ret['nvme0n1p1'].write_count == 5 - - def test_emulate_exclude_partitions(self): - # Make sure that when perdisk=False partitions (e.g. 'sda1', - # 'nvme0n1p1') are skipped and not included in the total count. - # https://github.com/giampaolo/psutil/pull/1313#issuecomment-408626842 - content = textwrap.dedent("""\ - 3 0 nvme0n1 1 2 3 4 5 6 7 8 9 10 11 - 3 0 nvme0n1p1 1 2 3 4 5 6 7 8 9 10 11 - """) - with mock_open_content({"/proc/diskstats": content}): - with mock.patch( - 'psutil._pslinux.is_storage_device', return_value=False - ): - ret = psutil.disk_io_counters(perdisk=False, nowrap=False) - assert ret is None - - def is_storage_device(name): - return name == 'nvme0n1' - - content = textwrap.dedent("""\ - 3 0 nvme0n1 1 2 3 4 5 6 7 8 9 10 11 - 3 0 nvme0n1p1 1 2 3 4 5 6 7 8 9 10 11 - """) - with mock_open_content({"/proc/diskstats": content}): - with mock.patch( - 'psutil._pslinux.is_storage_device', - create=True, - side_effect=is_storage_device, - ): - ret = psutil.disk_io_counters(perdisk=False, nowrap=False) - assert ret.read_count == 1 - assert ret.write_count == 5 - - def test_emulate_use_sysfs(self): - def exists(path): - return path == '/proc/diskstats' - - wprocfs = psutil.disk_io_counters(perdisk=True) - with mock.patch( - 'psutil._pslinux.os.path.exists', create=True, side_effect=exists - ): - wsysfs = psutil.disk_io_counters(perdisk=True) - assert len(wprocfs) == len(wsysfs) - - def test_emulate_not_impl(self): - def exists(path): - return False - - with mock.patch( - 'psutil._pslinux.os.path.exists', create=True, side_effect=exists - ): - with pytest.raises(NotImplementedError): - psutil.disk_io_counters() - - -@pytest.mark.skipif(not LINUX, reason="LINUX only") -class TestRootFsDeviceFinder(PsutilTestCase): - def setUp(self): - dev = os.stat("/").st_dev - self.major = os.major(dev) - self.minor = os.minor(dev) - - def test_call_methods(self): - finder = RootFsDeviceFinder() - if os.path.exists("/proc/partitions"): - finder.ask_proc_partitions() - else: - with pytest.raises(FileNotFoundError): - finder.ask_proc_partitions() - if os.path.exists(f"/sys/dev/block/{self.major}:{self.minor}/uevent"): - finder.ask_sys_dev_block() - else: - with pytest.raises(FileNotFoundError): - finder.ask_sys_dev_block() - finder.ask_sys_class_block() - - @pytest.mark.skipif(GITHUB_ACTIONS, reason="unsupported on GITHUB_ACTIONS") - def test_comparisons(self): - finder = RootFsDeviceFinder() - assert finder.find() is not None - - a = b = c = None - if os.path.exists("/proc/partitions"): - a = finder.ask_proc_partitions() - if os.path.exists(f"/sys/dev/block/{self.major}:{self.minor}/uevent"): - b = finder.ask_sys_class_block() - c = finder.ask_sys_dev_block() - - base = a or b or c - if base and a: - assert base == a - if base and b: - assert base == b - if base and c: - assert base == c - - @pytest.mark.skipif( - not shutil.which("findmnt"), reason="findmnt utility not available" - ) - @pytest.mark.skipif(GITHUB_ACTIONS, reason="unsupported on GITHUB_ACTIONS") - def test_against_findmnt(self): - psutil_value = RootFsDeviceFinder().find() - findmnt_value = sh("findmnt -o SOURCE -rn /") - assert psutil_value == findmnt_value - - def test_disk_partitions_mocked(self): - with mock.patch( - 'psutil._pslinux.cext.disk_partitions', - return_value=[('/dev/root', '/', 'ext4', 'rw')], - ) as m: - part = psutil.disk_partitions()[0] - assert m.called - if not GITHUB_ACTIONS: - assert part.device != "/dev/root" - assert part.device == RootFsDeviceFinder().find() - else: - assert part.device == "/dev/root" - - -# ===================================================================== -# --- misc -# ===================================================================== - - -@pytest.mark.skipif(not LINUX, reason="LINUX only") -class TestMisc(PsutilTestCase): - def test_boot_time(self): - vmstat_value = vmstat('boot time') - psutil_value = psutil.boot_time() - assert int(vmstat_value) == int(psutil_value) - - def test_no_procfs_on_import(self): - my_procfs = self.get_testfn() - os.mkdir(my_procfs) - - with open(os.path.join(my_procfs, 'stat'), 'w') as f: - f.write('cpu 0 0 0 0 0 0 0 0 0 0\n') - f.write('cpu0 0 0 0 0 0 0 0 0 0 0\n') - f.write('cpu1 0 0 0 0 0 0 0 0 0 0\n') - - try: - orig_open = open - - def open_mock(name, *args, **kwargs): - if name.startswith('/proc'): - raise FileNotFoundError - return orig_open(name, *args, **kwargs) - - with mock.patch("builtins.open", side_effect=open_mock): - reload_module(psutil) - - with pytest.raises(OSError): - psutil.cpu_times() - with pytest.raises(OSError): - psutil.cpu_times(percpu=True) - with pytest.raises(OSError): - psutil.cpu_percent() - with pytest.raises(OSError): - psutil.cpu_percent(percpu=True) - with pytest.raises(OSError): - psutil.cpu_times_percent() - with pytest.raises(OSError): - psutil.cpu_times_percent(percpu=True) - - psutil.PROCFS_PATH = my_procfs - - assert psutil.cpu_percent() == 0 - assert sum(psutil.cpu_times_percent()) == 0 - - # since we don't know the number of CPUs at import time, - # we awkwardly say there are none until the second call - per_cpu_percent = psutil.cpu_percent(percpu=True) - assert sum(per_cpu_percent) == 0 - - # ditto awkward length - per_cpu_times_percent = psutil.cpu_times_percent(percpu=True) - assert sum(map(sum, per_cpu_times_percent)) == 0 - - # much user, very busy - with open(os.path.join(my_procfs, 'stat'), 'w') as f: - f.write('cpu 1 0 0 0 0 0 0 0 0 0\n') - f.write('cpu0 1 0 0 0 0 0 0 0 0 0\n') - f.write('cpu1 1 0 0 0 0 0 0 0 0 0\n') - - assert psutil.cpu_percent() != 0 - assert sum(psutil.cpu_percent(percpu=True)) != 0 - assert sum(psutil.cpu_times_percent()) != 0 - assert ( - sum(map(sum, psutil.cpu_times_percent(percpu=True))) != 0 - ) - finally: - shutil.rmtree(my_procfs) - reload_module(psutil) - - assert psutil.PROCFS_PATH == '/proc' - - def test_cpu_steal_decrease(self): - # Test cumulative cpu stats decrease. We should ignore this. - # See issue #1210. - content = textwrap.dedent("""\ - cpu 0 0 0 0 0 0 0 1 0 0 - cpu0 0 0 0 0 0 0 0 1 0 0 - cpu1 0 0 0 0 0 0 0 1 0 0 - """).encode() - with mock_open_content({"/proc/stat": content}) as m: - # first call to "percent" functions should read the new stat file - # and compare to the "real" file read at import time - so the - # values are meaningless - psutil.cpu_percent() - assert m.called - psutil.cpu_percent(percpu=True) - psutil.cpu_times_percent() - psutil.cpu_times_percent(percpu=True) - - content = textwrap.dedent("""\ - cpu 1 0 0 0 0 0 0 0 0 0 - cpu0 1 0 0 0 0 0 0 0 0 0 - cpu1 1 0 0 0 0 0 0 0 0 0 - """).encode() - with mock_open_content({"/proc/stat": content}): - # Increase "user" while steal goes "backwards" to zero. - cpu_percent = psutil.cpu_percent() - assert m.called - cpu_percent_percpu = psutil.cpu_percent(percpu=True) - cpu_times_percent = psutil.cpu_times_percent() - cpu_times_percent_percpu = psutil.cpu_times_percent(percpu=True) - assert cpu_percent != 0 - assert sum(cpu_percent_percpu) != 0 - assert sum(cpu_times_percent) != 0 - assert sum(cpu_times_percent) != 100.0 - assert sum(map(sum, cpu_times_percent_percpu)) != 0 - assert sum(map(sum, cpu_times_percent_percpu)) != 100.0 - assert cpu_times_percent.steal == 0 - assert cpu_times_percent.user != 0 - - def test_boot_time_mocked(self): - with mock.patch('psutil._common.open', create=True) as m: - with pytest.raises(RuntimeError): - psutil._pslinux.boot_time() - assert m.called - - def test_users(self): - # Make sure the C extension converts ':0' and ':0.0' to - # 'localhost'. - for user in psutil.users(): - assert user.host not in {":0", ":0.0"} - - def test_procfs_path(self): - tdir = self.get_testfn() - os.mkdir(tdir) - try: - psutil.PROCFS_PATH = tdir - with pytest.raises(OSError): - psutil.virtual_memory() - with pytest.raises(OSError): - psutil.cpu_times() - with pytest.raises(OSError): - psutil.cpu_times(percpu=True) - with pytest.raises(OSError): - psutil.boot_time() - # self.assertRaises(OSError, psutil.pids) - with pytest.raises(OSError): - psutil.net_connections() - with pytest.raises(OSError): - psutil.net_io_counters() - with pytest.raises(OSError): - psutil.net_if_stats() - # self.assertRaises(OSError, psutil.disk_io_counters) - with pytest.raises(OSError): - psutil.disk_partitions() - with pytest.raises(psutil.NoSuchProcess): - psutil.Process() - finally: - psutil.PROCFS_PATH = "/proc" - - @retry_on_failure() - @pytest.mark.skipif(PYTEST_PARALLEL, reason="skip if pytest-parallel") - def test_issue_687(self): - # In case of thread ID: - # - pid_exists() is supposed to return False - # - Process(tid) is supposed to work - # - pids() should not return the TID - # See: https://github.com/giampaolo/psutil/issues/687 - with ThreadTask(): - p = psutil.Process() - threads = p.threads() - assert len(threads) == 2 - tid = sorted(threads, key=lambda x: x.id)[1].id - assert p.pid != tid - pt = psutil.Process(tid) - pt.as_dict() - assert tid not in psutil.pids() - - def test_pid_exists_no_proc_status(self): - # Internally pid_exists relies on /proc/{pid}/status. - # Emulate a case where this file is empty in which case - # psutil is supposed to fall back on using pids(). - with mock_open_content({"/proc/%s/status": ""}) as m: - assert psutil.pid_exists(os.getpid()) - assert m.called - - -# ===================================================================== -# --- sensors -# ===================================================================== - - -@pytest.mark.skipif(not LINUX, reason="LINUX only") -@pytest.mark.skipif(not HAS_BATTERY, reason="no battery") -class TestSensorsBattery(PsutilTestCase): - @pytest.mark.skipif( - not shutil.which("acpi"), reason="acpi utility not available" - ) - def test_percent(self): - out = sh("acpi -b") - acpi_value = int(out.split(",")[1].strip().replace('%', '')) - psutil_value = psutil.sensors_battery().percent - assert abs(acpi_value - psutil_value) < 1 - - def test_emulate_power_plugged(self): - # Pretend the AC power cable is connected. - def open_mock(name, *args, **kwargs): - if name.endswith(('AC0/online', 'AC/online')): - return io.BytesIO(b"1") - else: - return orig_open(name, *args, **kwargs) - - orig_open = open - with mock.patch("builtins.open", side_effect=open_mock) as m: - assert psutil.sensors_battery().power_plugged is True - assert ( - psutil.sensors_battery().secsleft - == psutil.POWER_TIME_UNLIMITED - ) - assert m.called - - def test_emulate_power_plugged_2(self): - # Same as above but pretend /AC0/online does not exist in which - # case code relies on /status file. - def open_mock(name, *args, **kwargs): - if name.endswith(('AC0/online', 'AC/online')): - raise FileNotFoundError - if name.endswith("/status"): - return io.StringIO("charging") - else: - return orig_open(name, *args, **kwargs) - - orig_open = open - with mock.patch("builtins.open", side_effect=open_mock) as m: - assert psutil.sensors_battery().power_plugged is True - assert m.called - - def test_emulate_power_not_plugged(self): - # Pretend the AC power cable is not connected. - def open_mock(name, *args, **kwargs): - if name.endswith(('AC0/online', 'AC/online')): - return io.BytesIO(b"0") - else: - return orig_open(name, *args, **kwargs) - - orig_open = open - with mock.patch("builtins.open", side_effect=open_mock) as m: - assert psutil.sensors_battery().power_plugged is False - assert m.called - - def test_emulate_power_not_plugged_2(self): - # Same as above but pretend /AC0/online does not exist in which - # case code relies on /status file. - def open_mock(name, *args, **kwargs): - if name.endswith(('AC0/online', 'AC/online')): - raise FileNotFoundError - if name.endswith("/status"): - return io.StringIO("discharging") - else: - return orig_open(name, *args, **kwargs) - - orig_open = open - with mock.patch("builtins.open", side_effect=open_mock) as m: - assert psutil.sensors_battery().power_plugged is False - assert m.called - - def test_emulate_power_undetermined(self): - # Pretend we can't know whether the AC power cable not - # connected (assert fallback to False). - def open_mock(name, *args, **kwargs): - if name.startswith(( - '/sys/class/power_supply/AC0/online', - '/sys/class/power_supply/AC/online', - )): - raise FileNotFoundError - if name.startswith("/sys/class/power_supply/BAT0/status"): - return io.BytesIO(b"???") - else: - return orig_open(name, *args, **kwargs) - - orig_open = open - with mock.patch("builtins.open", side_effect=open_mock) as m: - assert psutil.sensors_battery().power_plugged is None - assert m.called - - def test_emulate_energy_full_0(self): - # Emulate a case where energy_full files returns 0. - with mock_open_content( - {"/sys/class/power_supply/BAT0/energy_full": b"0"} - ) as m: - assert psutil.sensors_battery().percent == 0 - assert m.called - - def test_emulate_energy_full_not_avail(self): - # Emulate a case where energy_full file does not exist. - # Expected fallback on /capacity. - with mock_open_exception( - "/sys/class/power_supply/BAT0/energy_full", - FileNotFoundError, - ): - with mock_open_exception( - "/sys/class/power_supply/BAT0/charge_full", - FileNotFoundError, - ): - with mock_open_content( - {"/sys/class/power_supply/BAT0/capacity": b"88"} - ): - assert psutil.sensors_battery().percent == 88 - - def test_emulate_no_power(self): - # Emulate a case where /AC0/online file nor /BAT0/status exist. - with mock_open_exception( - "/sys/class/power_supply/AC/online", FileNotFoundError - ): - with mock_open_exception( - "/sys/class/power_supply/AC0/online", FileNotFoundError - ): - with mock_open_exception( - "/sys/class/power_supply/BAT0/status", - FileNotFoundError, - ): - assert psutil.sensors_battery().power_plugged is None - - -@pytest.mark.skipif(not LINUX, reason="LINUX only") -class TestSensorsBatteryEmulated(PsutilTestCase): - def test_it(self): - def open_mock(name, *args, **kwargs): - if name.endswith("/energy_now"): - return io.StringIO("60000000") - elif name.endswith("/power_now"): - return io.StringIO("0") - elif name.endswith("/energy_full"): - return io.StringIO("60000001") - else: - return orig_open(name, *args, **kwargs) - - orig_open = open - with mock.patch('os.listdir', return_value=["BAT0"]) as mlistdir: - with mock.patch("builtins.open", side_effect=open_mock) as mopen: - assert psutil.sensors_battery() is not None - assert mlistdir.called - assert mopen.called - - -@pytest.mark.skipif(not LINUX, reason="LINUX only") -class TestSensorsTemperatures(PsutilTestCase): - def test_emulate_class_hwmon(self): - def open_mock(name, *args, **kwargs): - if name.endswith('/name'): - return io.StringIO("name") - elif name.endswith('/temp1_label'): - return io.StringIO("label") - elif name.endswith('/temp1_input'): - return io.BytesIO(b"30000") - elif name.endswith('/temp1_max'): - return io.BytesIO(b"40000") - elif name.endswith('/temp1_crit'): - return io.BytesIO(b"50000") - else: - return orig_open(name, *args, **kwargs) - - orig_open = open - with mock.patch("builtins.open", side_effect=open_mock): - # Test case with /sys/class/hwmon - with mock.patch( - 'glob.glob', return_value=['/sys/class/hwmon/hwmon0/temp1'] - ): - temp = psutil.sensors_temperatures()['name'][0] - assert temp.label == 'label' - assert temp.current == 30.0 - assert temp.high == 40.0 - assert temp.critical == 50.0 - - def test_emulate_class_thermal(self): - def open_mock(name, *args, **kwargs): - if name.endswith('0_temp'): - return io.BytesIO(b"50000") - elif name.endswith('temp'): - return io.BytesIO(b"30000") - elif name.endswith('0_type'): - return io.StringIO("critical") - elif name.endswith('type'): - return io.StringIO("name") - else: - return orig_open(name, *args, **kwargs) - - def glob_mock(path): - if path in { - '/sys/class/hwmon/hwmon*/temp*_*', - '/sys/class/hwmon/hwmon*/device/temp*_*', - }: - return [] - elif path == '/sys/class/thermal/thermal_zone*': - return ['/sys/class/thermal/thermal_zone0'] - elif path == '/sys/class/thermal/thermal_zone0/trip_point*': - return [ - '/sys/class/thermal/thermal_zone1/trip_point_0_type', - '/sys/class/thermal/thermal_zone1/trip_point_0_temp', - ] - return [] - - orig_open = open - with mock.patch("builtins.open", side_effect=open_mock): - with mock.patch('glob.glob', create=True, side_effect=glob_mock): - temp = psutil.sensors_temperatures()['name'][0] - assert temp.label == '' - assert temp.current == 30.0 - assert temp.high == 50.0 - assert temp.critical == 50.0 - - -@pytest.mark.skipif(not LINUX, reason="LINUX only") -class TestSensorsFans(PsutilTestCase): - def test_emulate_data(self): - def open_mock(name, *args, **kwargs): - if name.endswith('/name'): - return io.StringIO("name") - elif name.endswith('/fan1_label'): - return io.StringIO("label") - elif name.endswith('/fan1_input'): - return io.StringIO("2000") - else: - return orig_open(name, *args, **kwargs) - - orig_open = open - with mock.patch("builtins.open", side_effect=open_mock): - with mock.patch( - 'glob.glob', return_value=['/sys/class/hwmon/hwmon2/fan1'] - ): - fan = psutil.sensors_fans()['name'][0] - assert fan.label == 'label' - assert fan.current == 2000 - - -# ===================================================================== -# --- test process -# ===================================================================== - - -@pytest.mark.skipif(not LINUX, reason="LINUX only") -class TestProcess(PsutilTestCase): - @retry_on_failure() - def test_parse_smaps_vs_memory_maps(self): - sproc = self.spawn_testproc() - uss, pss, swap = psutil._pslinux.Process(sproc.pid)._parse_smaps() - maps = psutil.Process(sproc.pid).memory_maps(grouped=False) - assert ( - abs(uss - sum(x.private_dirty + x.private_clean for x in maps)) - < 4096 - ) - assert abs(pss - sum(x.pss for x in maps)) < 4096 - assert abs(swap - sum(x.swap for x in maps)) < 4096 - - def test_parse_smaps_mocked(self): - # See: https://github.com/giampaolo/psutil/issues/1222 - content = textwrap.dedent("""\ - fffff0 r-xp 00000000 00:00 0 [vsyscall] - Size: 1 kB - Rss: 2 kB - Pss: 3 kB - Shared_Clean: 4 kB - Shared_Dirty: 5 kB - Private_Clean: 6 kB - Private_Dirty: 7 kB - Referenced: 8 kB - Anonymous: 9 kB - LazyFree: 10 kB - AnonHugePages: 11 kB - ShmemPmdMapped: 12 kB - Shared_Hugetlb: 13 kB - Private_Hugetlb: 14 kB - Swap: 15 kB - SwapPss: 16 kB - KernelPageSize: 17 kB - MMUPageSize: 18 kB - Locked: 19 kB - VmFlags: rd ex - """).encode() - with mock_open_content({f"/proc/{os.getpid()}/smaps": content}) as m: - p = psutil._pslinux.Process(os.getpid()) - uss, pss, swap = p._parse_smaps() - assert m.called - assert uss == (6 + 7 + 14) * 1024 - assert pss == 3 * 1024 - assert swap == 15 * 1024 - - # On PYPY file descriptors are not closed fast enough. - @pytest.mark.skipif(PYPY, reason="unreliable on PYPY") - def test_open_files_mode(self): - def get_test_file(fname): - p = psutil.Process() - giveup_at = time.time() + GLOBAL_TIMEOUT - while True: - for file in p.open_files(): - if file.path == os.path.abspath(fname): - return file - elif time.time() > giveup_at: - break - raise RuntimeError("timeout looking for test file") - - testfn = self.get_testfn() - with open(testfn, "w"): - assert get_test_file(testfn).mode == "w" - with open(testfn): - assert get_test_file(testfn).mode == "r" - with open(testfn, "a"): - assert get_test_file(testfn).mode == "a" - with open(testfn, "r+"): - assert get_test_file(testfn).mode == "r+" - with open(testfn, "w+"): - assert get_test_file(testfn).mode == "r+" - with open(testfn, "a+"): - assert get_test_file(testfn).mode == "a+" - - safe_rmpath(testfn) - with open(testfn, "x"): - assert get_test_file(testfn).mode == "w" - safe_rmpath(testfn) - with open(testfn, "x+"): - assert get_test_file(testfn).mode == "r+" - - def test_open_files_file_gone(self): - # simulates a file which gets deleted during open_files() - # execution - p = psutil.Process() - files = p.open_files() - with open(self.get_testfn(), 'w'): - # give the kernel some time to see the new file - call_until(lambda: len(p.open_files()) != len(files)) - with mock.patch( - 'psutil._pslinux.os.readlink', - side_effect=FileNotFoundError, - ) as m: - assert p.open_files() == [] - assert m.called - # also simulate the case where os.readlink() returns EINVAL - # in which case psutil is supposed to 'continue' - with mock.patch( - 'psutil._pslinux.os.readlink', - side_effect=OSError(errno.EINVAL, ""), - ) as m: - assert p.open_files() == [] - assert m.called - - def test_open_files_fd_gone(self): - # Simulate a case where /proc/{pid}/fdinfo/{fd} disappears - # while iterating through fds. - # https://travis-ci.org/giampaolo/psutil/jobs/225694530 - p = psutil.Process() - files = p.open_files() - with open(self.get_testfn(), 'w'): - # give the kernel some time to see the new file - call_until(lambda: len(p.open_files()) != len(files)) - with mock.patch( - "builtins.open", side_effect=FileNotFoundError - ) as m: - assert p.open_files() == [] - assert m.called - - def test_open_files_enametoolong(self): - # Simulate a case where /proc/{pid}/fd/{fd} symlink - # points to a file with full path longer than PATH_MAX, see: - # https://github.com/giampaolo/psutil/issues/1940 - p = psutil.Process() - files = p.open_files() - with open(self.get_testfn(), 'w'): - # give the kernel some time to see the new file - call_until(lambda: len(p.open_files()) != len(files)) - patch_point = 'psutil._pslinux.os.readlink' - with mock.patch( - patch_point, side_effect=OSError(errno.ENAMETOOLONG, "") - ) as m: - with mock.patch("psutil._pslinux.debug"): - assert p.open_files() == [] - assert m.called - - # --- mocked tests - - def test_terminal_mocked(self): - with mock.patch( - 'psutil._pslinux._psposix.get_terminal_map', return_value={} - ) as m: - assert psutil._pslinux.Process(os.getpid()).terminal() is None - assert m.called - - # TODO: re-enable this test. - # def test_num_ctx_switches_mocked(self): - # with mock.patch('psutil._common.open', create=True) as m: - # self.assertRaises( - # NotImplementedError, - # psutil._pslinux.Process(os.getpid()).num_ctx_switches) - # assert m.called - - def test_cmdline_mocked(self): - # see: https://github.com/giampaolo/psutil/issues/639 - p = psutil.Process() - fake_file = io.StringIO('foo\x00bar\x00') - with mock.patch( - 'psutil._common.open', return_value=fake_file, create=True - ) as m: - assert p.cmdline() == ['foo', 'bar'] - assert m.called - fake_file = io.StringIO('foo\x00bar\x00\x00') - with mock.patch( - 'psutil._common.open', return_value=fake_file, create=True - ) as m: - assert p.cmdline() == ['foo', 'bar', ''] - assert m.called - - def test_cmdline_spaces_mocked(self): - # see: https://github.com/giampaolo/psutil/issues/1179 - p = psutil.Process() - fake_file = io.StringIO('foo bar ') - with mock.patch( - 'psutil._common.open', return_value=fake_file, create=True - ) as m: - assert p.cmdline() == ['foo', 'bar'] - assert m.called - fake_file = io.StringIO('foo bar ') - with mock.patch( - 'psutil._common.open', return_value=fake_file, create=True - ) as m: - assert p.cmdline() == ['foo', 'bar', ''] - assert m.called - - def test_cmdline_mixed_separators(self): - # https://github.com/giampaolo/psutil/issues/ - # 1179#issuecomment-552984549 - p = psutil.Process() - fake_file = io.StringIO('foo\x20bar\x00') - with mock.patch( - 'psutil._common.open', return_value=fake_file, create=True - ) as m: - assert p.cmdline() == ['foo', 'bar'] - assert m.called - - def test_readlink_path_deleted_mocked(self): - with mock.patch( - 'psutil._pslinux.os.readlink', return_value='/home/foo (deleted)' - ): - assert psutil.Process().exe() == "/home/foo" - assert psutil.Process().cwd() == "/home/foo" - - def test_threads_mocked(self): - # Test the case where os.listdir() returns a file (thread) - # which no longer exists by the time we open() it (race - # condition). threads() is supposed to ignore that instead - # of raising NSP. - def open_mock_1(name, *args, **kwargs): - if name.startswith(f"/proc/{os.getpid()}/task"): - raise FileNotFoundError - return orig_open(name, *args, **kwargs) - - orig_open = open - with mock.patch("builtins.open", side_effect=open_mock_1) as m: - ret = psutil.Process().threads() - assert m.called - assert ret == [] - - # ...but if it bumps into something != ENOENT we want an - # exception. - def open_mock_2(name, *args, **kwargs): - if name.startswith(f"/proc/{os.getpid()}/task"): - raise PermissionError - return orig_open(name, *args, **kwargs) - - with mock.patch("builtins.open", side_effect=open_mock_2): - with pytest.raises(psutil.AccessDenied): - psutil.Process().threads() - - def test_exe_mocked(self): - with mock.patch( - 'psutil._pslinux.readlink', side_effect=FileNotFoundError - ) as m: - # de-activate guessing from cmdline() - with mock.patch( - 'psutil._pslinux.Process.cmdline', return_value=[] - ): - ret = psutil.Process().exe() - assert m.called - assert ret == "" - - def test_issue_1014(self): - # Emulates a case where smaps file does not exist. In this case - # wrap_exception decorator should not raise NoSuchProcess. - with mock_open_exception( - f"/proc/{os.getpid()}/smaps", FileNotFoundError - ) as m: - p = psutil.Process() - with pytest.raises(FileNotFoundError): - p.memory_maps() - assert m.called - - def test_issue_2418(self): - p = psutil.Process() - with mock_open_exception( - f"/proc/{os.getpid()}/statm", FileNotFoundError - ): - with mock.patch("os.path.exists", return_value=False): - with pytest.raises(psutil.NoSuchProcess): - p.memory_info() - - @pytest.mark.skipif(not HAS_RLIMIT, reason="not supported") - def test_rlimit_zombie(self): - # Emulate a case where rlimit() raises ENOSYS, which may - # happen in case of zombie process: - # https://travis-ci.org/giampaolo/psutil/jobs/51368273 - with mock.patch( - "resource.prlimit", side_effect=OSError(errno.ENOSYS, "") - ) as m1: - with mock.patch( - "psutil._pslinux.Process._is_zombie", return_value=True - ) as m2: - p = psutil.Process() - p.name() - with pytest.raises(psutil.ZombieProcess) as cm: - p.rlimit(psutil.RLIMIT_NOFILE) - assert m1.called - assert m2.called - assert cm.value.pid == p.pid - assert cm.value.name == p.name() - - def test_stat_file_parsing(self): - args = [ - "0", # pid - "(cat)", # name - "Z", # status - "1", # ppid - "0", # pgrp - "0", # session - "0", # tty - "0", # tpgid - "0", # flags - "0", # minflt - "0", # cminflt - "0", # majflt - "0", # cmajflt - "2", # utime - "3", # stime - "4", # cutime - "5", # cstime - "0", # priority - "0", # nice - "0", # num_threads - "0", # itrealvalue - "6", # starttime - "0", # vsize - "0", # rss - "0", # rsslim - "0", # startcode - "0", # endcode - "0", # startstack - "0", # kstkesp - "0", # kstkeip - "0", # signal - "0", # blocked - "0", # sigignore - "0", # sigcatch - "0", # wchan - "0", # nswap - "0", # cnswap - "0", # exit_signal - "6", # processor - "0", # rt priority - "0", # policy - "7", # delayacct_blkio_ticks - ] - content = " ".join(args).encode() - with mock_open_content({f"/proc/{os.getpid()}/stat": content}): - p = psutil.Process() - assert p.name() == 'cat' - assert p.status() == psutil.STATUS_ZOMBIE - assert p.ppid() == 1 - assert p.create_time() == 6 / CLOCK_TICKS + psutil.boot_time() - cpu = p.cpu_times() - assert cpu.user == 2 / CLOCK_TICKS - assert cpu.system == 3 / CLOCK_TICKS - assert cpu.children_user == 4 / CLOCK_TICKS - assert cpu.children_system == 5 / CLOCK_TICKS - assert cpu.iowait == 7 / CLOCK_TICKS - assert p.cpu_num() == 6 - - def test_status_file_parsing(self): - content = textwrap.dedent("""\ - Uid:\t1000\t1001\t1002\t1003 - Gid:\t1004\t1005\t1006\t1007 - Threads:\t66 - Cpus_allowed:\tf - Cpus_allowed_list:\t0-7 - voluntary_ctxt_switches:\t12 - nonvoluntary_ctxt_switches:\t13""").encode() - with mock_open_content({f"/proc/{os.getpid()}/status": content}): - p = psutil.Process() - assert p.num_ctx_switches().voluntary == 12 - assert p.num_ctx_switches().involuntary == 13 - assert p.num_threads() == 66 - uids = p.uids() - assert uids.real == 1000 - assert uids.effective == 1001 - assert uids.saved == 1002 - gids = p.gids() - assert gids.real == 1004 - assert gids.effective == 1005 - assert gids.saved == 1006 - assert p._proc._get_eligible_cpus() == list(range(8)) - - def test_net_connections_enametoolong(self): - # Simulate a case where /proc/{pid}/fd/{fd} symlink points to - # a file with full path longer than PATH_MAX, see: - # https://github.com/giampaolo/psutil/issues/1940 - with mock.patch( - 'psutil._pslinux.os.readlink', - side_effect=OSError(errno.ENAMETOOLONG, ""), - ) as m: - p = psutil.Process() - with mock.patch("psutil._pslinux.debug"): - assert p.net_connections() == [] - assert m.called - - -@pytest.mark.skipif(not LINUX, reason="LINUX only") -class TestProcessAgainstStatus(PsutilTestCase): - """/proc/pid/stat and /proc/pid/status have many values in common. - Whenever possible, psutil uses /proc/pid/stat (it's faster). - For all those cases we check that the value found in - /proc/pid/stat (by psutil) matches the one found in - /proc/pid/status. - """ - - @classmethod - def setUpClass(cls): - cls.proc = psutil.Process() - - def read_status_file(self, linestart): - with psutil._psplatform.open_text( - f"/proc/{self.proc.pid}/status" - ) as f: - for line in f: - line = line.strip() - if line.startswith(linestart): - value = line.partition('\t')[2] - try: - return int(value) - except ValueError: - return value - raise ValueError(f"can't find {linestart!r}") - - def test_name(self): - value = self.read_status_file("Name:") - assert self.proc.name() == value - - def test_status(self): - value = self.read_status_file("State:") - value = value[value.find('(') + 1 : value.rfind(')')] - value = value.replace(' ', '-') - assert self.proc.status() == value - - def test_ppid(self): - value = self.read_status_file("PPid:") - assert self.proc.ppid() == value - - def test_num_threads(self): - value = self.read_status_file("Threads:") - assert self.proc.num_threads() == value - - def test_uids(self): - value = self.read_status_file("Uid:") - value = tuple(map(int, value.split()[1:4])) - assert self.proc.uids() == value - - def test_gids(self): - value = self.read_status_file("Gid:") - value = tuple(map(int, value.split()[1:4])) - assert self.proc.gids() == value - - @retry_on_failure() - def test_num_ctx_switches(self): - value = self.read_status_file("voluntary_ctxt_switches:") - assert self.proc.num_ctx_switches().voluntary == value - value = self.read_status_file("nonvoluntary_ctxt_switches:") - assert self.proc.num_ctx_switches().involuntary == value - - def test_cpu_affinity(self): - value = self.read_status_file("Cpus_allowed_list:") - if '-' in str(value): - min_, max_ = map(int, value.split('-')) - assert self.proc.cpu_affinity() == list(range(min_, max_ + 1)) - - def test_cpu_affinity_eligible_cpus(self): - value = self.read_status_file("Cpus_allowed_list:") - with mock.patch("psutil._pslinux.per_cpu_times") as m: - self.proc._proc._get_eligible_cpus() - if '-' in str(value): - assert not m.called - else: - assert m.called - - -# ===================================================================== -# --- test utils -# ===================================================================== - - -@pytest.mark.skipif(not LINUX, reason="LINUX only") -class TestUtils(PsutilTestCase): - def test_readlink(self): - with mock.patch("os.readlink", return_value="foo (deleted)") as m: - assert psutil._psplatform.readlink("bar") == "foo" - assert m.called diff --git a/PortablePython/Lib/site-packages/psutil/tests/test_memleaks.py b/PortablePython/Lib/site-packages/psutil/tests/test_memleaks.py deleted file mode 100644 index 7f78fae..0000000 --- a/PortablePython/Lib/site-packages/psutil/tests/test_memleaks.py +++ /dev/null @@ -1,487 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -"""Tests for detecting function memory leaks (typically the ones -implemented in C). It does so by calling a function many times and -checking whether process memory usage keeps increasing between -calls or over time. -Note that this may produce false positives (especially on Windows -for some reason). -PyPy appears to be completely unstable for this framework, probably -because of how its JIT handles memory, so tests are skipped. -""" - - -import functools -import os -import platform - -import psutil -import psutil._common -from psutil import LINUX -from psutil import MACOS -from psutil import OPENBSD -from psutil import POSIX -from psutil import SUNOS -from psutil import WINDOWS -from psutil.tests import HAS_CPU_AFFINITY -from psutil.tests import HAS_CPU_FREQ -from psutil.tests import HAS_ENVIRON -from psutil.tests import HAS_IONICE -from psutil.tests import HAS_MEMORY_MAPS -from psutil.tests import HAS_NET_IO_COUNTERS -from psutil.tests import HAS_PROC_CPU_NUM -from psutil.tests import HAS_PROC_IO_COUNTERS -from psutil.tests import HAS_RLIMIT -from psutil.tests import HAS_SENSORS_BATTERY -from psutil.tests import HAS_SENSORS_FANS -from psutil.tests import HAS_SENSORS_TEMPERATURES -from psutil.tests import TestMemoryLeak -from psutil.tests import create_sockets -from psutil.tests import get_testfn -from psutil.tests import process_namespace -from psutil.tests import pytest -from psutil.tests import skip_on_access_denied -from psutil.tests import spawn_testproc -from psutil.tests import system_namespace -from psutil.tests import terminate - - -cext = psutil._psplatform.cext -thisproc = psutil.Process() -FEW_TIMES = 5 - - -def fewtimes_if_linux(): - """Decorator for those Linux functions which are implemented in pure - Python, and which we want to run faster. - """ - - def decorator(fun): - @functools.wraps(fun) - def wrapper(self, *args, **kwargs): - if LINUX: - before = self.__class__.times - try: - self.__class__.times = FEW_TIMES - return fun(self, *args, **kwargs) - finally: - self.__class__.times = before - else: - return fun(self, *args, **kwargs) - - return wrapper - - return decorator - - -# =================================================================== -# Process class -# =================================================================== - - -class TestProcessObjectLeaks(TestMemoryLeak): - """Test leaks of Process class methods.""" - - proc = thisproc - - def test_coverage(self): - ns = process_namespace(None) - ns.test_class_coverage(self, ns.getters + ns.setters) - - @fewtimes_if_linux() - def test_name(self): - self.execute(self.proc.name) - - @fewtimes_if_linux() - def test_cmdline(self): - self.execute(self.proc.cmdline) - - @fewtimes_if_linux() - def test_exe(self): - self.execute(self.proc.exe) - - @fewtimes_if_linux() - def test_ppid(self): - self.execute(self.proc.ppid) - - @pytest.mark.skipif(not POSIX, reason="POSIX only") - @fewtimes_if_linux() - def test_uids(self): - self.execute(self.proc.uids) - - @pytest.mark.skipif(not POSIX, reason="POSIX only") - @fewtimes_if_linux() - def test_gids(self): - self.execute(self.proc.gids) - - @fewtimes_if_linux() - def test_status(self): - self.execute(self.proc.status) - - def test_nice(self): - self.execute(self.proc.nice) - - def test_nice_set(self): - niceness = thisproc.nice() - self.execute(lambda: self.proc.nice(niceness)) - - @pytest.mark.skipif(not HAS_IONICE, reason="not supported") - def test_ionice(self): - self.execute(self.proc.ionice) - - @pytest.mark.skipif(not HAS_IONICE, reason="not supported") - def test_ionice_set(self): - if WINDOWS: - value = thisproc.ionice() - self.execute(lambda: self.proc.ionice(value)) - else: - self.execute(lambda: self.proc.ionice(psutil.IOPRIO_CLASS_NONE)) - fun = functools.partial(cext.proc_ioprio_set, os.getpid(), -1, 0) - self.execute_w_exc(OSError, fun) - - @pytest.mark.skipif(not HAS_PROC_IO_COUNTERS, reason="not supported") - @fewtimes_if_linux() - def test_io_counters(self): - self.execute(self.proc.io_counters) - - @pytest.mark.skipif(POSIX, reason="worthless on POSIX") - def test_username(self): - # always open 1 handle on Windows (only once) - psutil.Process().username() - self.execute(self.proc.username) - - @fewtimes_if_linux() - def test_create_time(self): - self.execute(self.proc.create_time) - - @fewtimes_if_linux() - @skip_on_access_denied(only_if=OPENBSD) - def test_num_threads(self): - self.execute(self.proc.num_threads) - - @pytest.mark.skipif(not WINDOWS, reason="WINDOWS only") - def test_num_handles(self): - self.execute(self.proc.num_handles) - - @pytest.mark.skipif(not POSIX, reason="POSIX only") - @fewtimes_if_linux() - def test_num_fds(self): - self.execute(self.proc.num_fds) - - @fewtimes_if_linux() - def test_num_ctx_switches(self): - self.execute(self.proc.num_ctx_switches) - - @fewtimes_if_linux() - @skip_on_access_denied(only_if=OPENBSD) - def test_threads(self): - self.execute(self.proc.threads) - - @fewtimes_if_linux() - def test_cpu_times(self): - self.execute(self.proc.cpu_times) - - @fewtimes_if_linux() - @pytest.mark.skipif(not HAS_PROC_CPU_NUM, reason="not supported") - def test_cpu_num(self): - self.execute(self.proc.cpu_num) - - @fewtimes_if_linux() - def test_memory_info(self): - self.execute(self.proc.memory_info) - - @fewtimes_if_linux() - def test_memory_full_info(self): - self.execute(self.proc.memory_full_info) - - @pytest.mark.skipif(not POSIX, reason="POSIX only") - @fewtimes_if_linux() - def test_terminal(self): - self.execute(self.proc.terminal) - - def test_resume(self): - times = FEW_TIMES if POSIX else self.times - self.execute(self.proc.resume, times=times) - - @fewtimes_if_linux() - def test_cwd(self): - self.execute(self.proc.cwd) - - @pytest.mark.skipif(not HAS_CPU_AFFINITY, reason="not supported") - def test_cpu_affinity(self): - self.execute(self.proc.cpu_affinity) - - @pytest.mark.skipif(not HAS_CPU_AFFINITY, reason="not supported") - def test_cpu_affinity_set(self): - affinity = thisproc.cpu_affinity() - self.execute(lambda: self.proc.cpu_affinity(affinity)) - self.execute_w_exc(ValueError, lambda: self.proc.cpu_affinity([-1])) - - @fewtimes_if_linux() - def test_open_files(self): - with open(get_testfn(), 'w'): - self.execute(self.proc.open_files) - - @pytest.mark.skipif(not HAS_MEMORY_MAPS, reason="not supported") - @fewtimes_if_linux() - def test_memory_maps(self): - self.execute(self.proc.memory_maps) - - @pytest.mark.skipif(not LINUX, reason="LINUX only") - @pytest.mark.skipif(not HAS_RLIMIT, reason="not supported") - def test_rlimit(self): - self.execute(lambda: self.proc.rlimit(psutil.RLIMIT_NOFILE)) - - @pytest.mark.skipif(not LINUX, reason="LINUX only") - @pytest.mark.skipif(not HAS_RLIMIT, reason="not supported") - def test_rlimit_set(self): - limit = thisproc.rlimit(psutil.RLIMIT_NOFILE) - self.execute(lambda: self.proc.rlimit(psutil.RLIMIT_NOFILE, limit)) - self.execute_w_exc((OSError, ValueError), lambda: self.proc.rlimit(-1)) - - @fewtimes_if_linux() - # Windows implementation is based on a single system-wide - # function (tested later). - @pytest.mark.skipif(WINDOWS, reason="worthless on WINDOWS") - def test_net_connections(self): - # TODO: UNIX sockets are temporarily implemented by parsing - # 'pfiles' cmd output; we don't want that part of the code to - # be executed. - with create_sockets(): - kind = 'inet' if SUNOS else 'all' - self.execute(lambda: self.proc.net_connections(kind)) - - @pytest.mark.skipif(not HAS_ENVIRON, reason="not supported") - def test_environ(self): - self.execute(self.proc.environ) - - @pytest.mark.skipif(not WINDOWS, reason="WINDOWS only") - def test_proc_info(self): - self.execute(lambda: cext.proc_info(os.getpid())) - - -class TestTerminatedProcessLeaks(TestProcessObjectLeaks): - """Repeat the tests above looking for leaks occurring when dealing - with terminated processes raising NoSuchProcess exception. - The C functions are still invoked but will follow different code - paths. We'll check those code paths. - """ - - @classmethod - def setUpClass(cls): - super().setUpClass() - cls.subp = spawn_testproc() - cls.proc = psutil.Process(cls.subp.pid) - cls.proc.kill() - cls.proc.wait() - - @classmethod - def tearDownClass(cls): - super().tearDownClass() - terminate(cls.subp) - - def call(self, fun): - try: - fun() - except psutil.NoSuchProcess: - pass - - if WINDOWS: - - def test_kill(self): - self.execute(self.proc.kill) - - def test_terminate(self): - self.execute(self.proc.terminate) - - def test_suspend(self): - self.execute(self.proc.suspend) - - def test_resume(self): - self.execute(self.proc.resume) - - def test_wait(self): - self.execute(self.proc.wait) - - def test_proc_info(self): - # test dual implementation - def call(): - try: - return cext.proc_info(self.proc.pid) - except ProcessLookupError: - pass - - self.execute(call) - - -@pytest.mark.skipif(not WINDOWS, reason="WINDOWS only") -class TestProcessDualImplementation(TestMemoryLeak): - def test_cmdline_peb_true(self): - self.execute(lambda: cext.proc_cmdline(os.getpid(), use_peb=True)) - - def test_cmdline_peb_false(self): - self.execute(lambda: cext.proc_cmdline(os.getpid(), use_peb=False)) - - -# =================================================================== -# system APIs -# =================================================================== - - -class TestModuleFunctionsLeaks(TestMemoryLeak): - """Test leaks of psutil module functions.""" - - def test_coverage(self): - ns = system_namespace() - ns.test_class_coverage(self, ns.all) - - # --- cpu - - @fewtimes_if_linux() - def test_cpu_count(self): # logical - self.execute(lambda: psutil.cpu_count(logical=True)) - - @fewtimes_if_linux() - def test_cpu_count_cores(self): - self.execute(lambda: psutil.cpu_count(logical=False)) - - @fewtimes_if_linux() - def test_cpu_times(self): - self.execute(psutil.cpu_times) - - @fewtimes_if_linux() - def test_per_cpu_times(self): - self.execute(lambda: psutil.cpu_times(percpu=True)) - - @fewtimes_if_linux() - def test_cpu_stats(self): - self.execute(psutil.cpu_stats) - - @fewtimes_if_linux() - # TODO: remove this once 1892 is fixed - @pytest.mark.skipif( - MACOS and platform.machine() == 'arm64', reason="skipped due to #1892" - ) - @pytest.mark.skipif(not HAS_CPU_FREQ, reason="not supported") - def test_cpu_freq(self): - self.execute(psutil.cpu_freq) - - @pytest.mark.skipif(not WINDOWS, reason="WINDOWS only") - def test_getloadavg(self): - psutil.getloadavg() - self.execute(psutil.getloadavg) - - # --- mem - - def test_virtual_memory(self): - self.execute(psutil.virtual_memory) - - # TODO: remove this skip when this gets fixed - @pytest.mark.skipif(SUNOS, reason="worthless on SUNOS (uses a subprocess)") - def test_swap_memory(self): - self.execute(psutil.swap_memory) - - def test_pid_exists(self): - times = FEW_TIMES if POSIX else self.times - self.execute(lambda: psutil.pid_exists(os.getpid()), times=times) - - # --- disk - - def test_disk_usage(self): - times = FEW_TIMES if POSIX else self.times - self.execute(lambda: psutil.disk_usage('.'), times=times) - - def test_disk_partitions(self): - self.execute(psutil.disk_partitions) - - @pytest.mark.skipif( - LINUX and not os.path.exists('/proc/diskstats'), - reason="/proc/diskstats not available on this Linux version", - ) - @fewtimes_if_linux() - def test_disk_io_counters(self): - self.execute(lambda: psutil.disk_io_counters(nowrap=False)) - - # --- proc - - @fewtimes_if_linux() - def test_pids(self): - self.execute(psutil.pids) - - # --- net - - @fewtimes_if_linux() - @pytest.mark.skipif(not HAS_NET_IO_COUNTERS, reason="not supported") - def test_net_io_counters(self): - self.execute(lambda: psutil.net_io_counters(nowrap=False)) - - @fewtimes_if_linux() - @pytest.mark.skipif(MACOS and os.getuid() != 0, reason="need root access") - def test_net_connections(self): - # always opens and handle on Windows() (once) - psutil.net_connections(kind='all') - with create_sockets(): - self.execute(lambda: psutil.net_connections(kind='all')) - - def test_net_if_addrs(self): - # Note: verified that on Windows this was a false positive. - tolerance = 80 * 1024 if WINDOWS else self.tolerance - self.execute(psutil.net_if_addrs, tolerance=tolerance) - - def test_net_if_stats(self): - self.execute(psutil.net_if_stats) - - # --- sensors - - @fewtimes_if_linux() - @pytest.mark.skipif(not HAS_SENSORS_BATTERY, reason="not supported") - def test_sensors_battery(self): - self.execute(psutil.sensors_battery) - - @fewtimes_if_linux() - @pytest.mark.skipif(not HAS_SENSORS_TEMPERATURES, reason="not supported") - def test_sensors_temperatures(self): - self.execute(psutil.sensors_temperatures) - - @fewtimes_if_linux() - @pytest.mark.skipif(not HAS_SENSORS_FANS, reason="not supported") - def test_sensors_fans(self): - self.execute(psutil.sensors_fans) - - # --- others - - @fewtimes_if_linux() - def test_boot_time(self): - self.execute(psutil.boot_time) - - def test_users(self): - self.execute(psutil.users) - - def test_set_debug(self): - self.execute(lambda: psutil._set_debug(False)) - - if WINDOWS: - - # --- win services - - def test_win_service_iter(self): - self.execute(cext.winservice_enumerate) - - def test_win_service_get(self): - pass - - def test_win_service_get_config(self): - name = next(psutil.win_service_iter()).name() - self.execute(lambda: cext.winservice_query_config(name)) - - def test_win_service_get_status(self): - name = next(psutil.win_service_iter()).name() - self.execute(lambda: cext.winservice_query_status(name)) - - def test_win_service_get_description(self): - name = next(psutil.win_service_iter()).name() - self.execute(lambda: cext.winservice_query_descr(name)) diff --git a/PortablePython/Lib/site-packages/psutil/tests/test_misc.py b/PortablePython/Lib/site-packages/psutil/tests/test_misc.py deleted file mode 100644 index c484264..0000000 --- a/PortablePython/Lib/site-packages/psutil/tests/test_misc.py +++ /dev/null @@ -1,873 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -"""Miscellaneous tests.""" - -import collections -import contextlib -import io -import json -import os -import pickle -import socket -import sys -from unittest import mock - -import psutil -import psutil.tests -from psutil import WINDOWS -from psutil._common import bcat -from psutil._common import cat -from psutil._common import debug -from psutil._common import isfile_strict -from psutil._common import memoize -from psutil._common import memoize_when_activated -from psutil._common import parse_environ_block -from psutil._common import supports_ipv6 -from psutil._common import wrap_numbers -from psutil.tests import HAS_NET_IO_COUNTERS -from psutil.tests import PsutilTestCase -from psutil.tests import process_namespace -from psutil.tests import pytest -from psutil.tests import reload_module -from psutil.tests import system_namespace - - -# =================================================================== -# --- Test classes' repr(), str(), ... -# =================================================================== - - -class TestSpecialMethods(PsutilTestCase): - def test_check_pid_range(self): - with pytest.raises(OverflowError): - psutil._psplatform.cext.check_pid_range(2**128) - with pytest.raises(psutil.NoSuchProcess): - psutil.Process(2**128) - - def test_process__repr__(self, func=repr): - p = psutil.Process(self.spawn_testproc().pid) - r = func(p) - assert "psutil.Process" in r - assert f"pid={p.pid}" in r - assert f"name='{p.name()}'" in r.replace("name=u'", "name='") - assert "status=" in r - assert "exitcode=" not in r - p.terminate() - p.wait() - r = func(p) - assert "status='terminated'" in r - assert "exitcode=" in r - - with mock.patch.object( - psutil.Process, - "name", - side_effect=psutil.ZombieProcess(os.getpid()), - ): - p = psutil.Process() - r = func(p) - assert f"pid={p.pid}" in r - assert "status='zombie'" in r - assert "name=" not in r - with mock.patch.object( - psutil.Process, - "name", - side_effect=psutil.NoSuchProcess(os.getpid()), - ): - p = psutil.Process() - r = func(p) - assert f"pid={p.pid}" in r - assert "terminated" in r - assert "name=" not in r - with mock.patch.object( - psutil.Process, - "name", - side_effect=psutil.AccessDenied(os.getpid()), - ): - p = psutil.Process() - r = func(p) - assert f"pid={p.pid}" in r - assert "name=" not in r - - def test_process__str__(self): - self.test_process__repr__(func=str) - - def test_error__repr__(self): - assert repr(psutil.Error()) == "psutil.Error()" - - def test_error__str__(self): - assert str(psutil.Error()) == "" - - def test_no_such_process__repr__(self): - assert ( - repr(psutil.NoSuchProcess(321)) - == "psutil.NoSuchProcess(pid=321, msg='process no longer exists')" - ) - assert ( - repr(psutil.NoSuchProcess(321, name="name", msg="msg")) - == "psutil.NoSuchProcess(pid=321, name='name', msg='msg')" - ) - - def test_no_such_process__str__(self): - assert ( - str(psutil.NoSuchProcess(321)) - == "process no longer exists (pid=321)" - ) - assert ( - str(psutil.NoSuchProcess(321, name="name", msg="msg")) - == "msg (pid=321, name='name')" - ) - - def test_zombie_process__repr__(self): - assert ( - repr(psutil.ZombieProcess(321)) - == 'psutil.ZombieProcess(pid=321, msg="PID still ' - 'exists but it\'s a zombie")' - ) - assert ( - repr(psutil.ZombieProcess(321, name="name", ppid=320, msg="foo")) - == "psutil.ZombieProcess(pid=321, ppid=320, name='name'," - " msg='foo')" - ) - - def test_zombie_process__str__(self): - assert ( - str(psutil.ZombieProcess(321)) - == "PID still exists but it's a zombie (pid=321)" - ) - assert ( - str(psutil.ZombieProcess(321, name="name", ppid=320, msg="foo")) - == "foo (pid=321, ppid=320, name='name')" - ) - - def test_access_denied__repr__(self): - assert repr(psutil.AccessDenied(321)) == "psutil.AccessDenied(pid=321)" - assert ( - repr(psutil.AccessDenied(321, name="name", msg="msg")) - == "psutil.AccessDenied(pid=321, name='name', msg='msg')" - ) - - def test_access_denied__str__(self): - assert str(psutil.AccessDenied(321)) == "(pid=321)" - assert ( - str(psutil.AccessDenied(321, name="name", msg="msg")) - == "msg (pid=321, name='name')" - ) - - def test_timeout_expired__repr__(self): - assert ( - repr(psutil.TimeoutExpired(5)) - == "psutil.TimeoutExpired(seconds=5, msg='timeout after 5" - " seconds')" - ) - assert ( - repr(psutil.TimeoutExpired(5, pid=321, name="name")) - == "psutil.TimeoutExpired(pid=321, name='name', seconds=5, " - "msg='timeout after 5 seconds')" - ) - - def test_timeout_expired__str__(self): - assert str(psutil.TimeoutExpired(5)) == "timeout after 5 seconds" - assert ( - str(psutil.TimeoutExpired(5, pid=321, name="name")) - == "timeout after 5 seconds (pid=321, name='name')" - ) - - def test_process__eq__(self): - p1 = psutil.Process() - p2 = psutil.Process() - assert p1 == p2 - p2._ident = (0, 0) - assert p1 != p2 - assert p1 != 'foo' - - def test_process__hash__(self): - s = {psutil.Process(), psutil.Process()} - assert len(s) == 1 - - -# =================================================================== -# --- Misc, generic, corner cases -# =================================================================== - - -class TestMisc(PsutilTestCase): - def test__all__(self): - dir_psutil = dir(psutil) - for name in dir_psutil: - if name in { - 'debug', - 'tests', - 'test', - 'PermissionError', - 'ProcessLookupError', - }: - continue - if not name.startswith('_'): - try: - __import__(name) - except ImportError: - if name not in psutil.__all__: - fun = getattr(psutil, name) - if fun is None: - continue - if ( - fun.__doc__ is not None - and 'deprecated' not in fun.__doc__.lower() - ): - raise self.fail(f"{name!r} not in psutil.__all__") - - # Import 'star' will break if __all__ is inconsistent, see: - # https://github.com/giampaolo/psutil/issues/656 - # Can't do `from psutil import *` as it won't work - # so we simply iterate over __all__. - for name in psutil.__all__: - assert name in dir_psutil - - def test_version(self): - assert ( - '.'.join([str(x) for x in psutil.version_info]) - == psutil.__version__ - ) - - def test_process_as_dict_no_new_names(self): - # See https://github.com/giampaolo/psutil/issues/813 - p = psutil.Process() - p.foo = '1' - assert 'foo' not in p.as_dict() - - def test_serialization(self): - def check(ret): - json.loads(json.dumps(ret)) - - a = pickle.dumps(ret) - b = pickle.loads(a) - assert ret == b - - # --- process APIs - - proc = psutil.Process() - check(psutil.Process().as_dict()) - - ns = process_namespace(proc) - for fun, name in ns.iter(ns.getters, clear_cache=True): - with self.subTest(proc=proc, name=name): - try: - ret = fun() - except psutil.Error: - pass - else: - check(ret) - - # --- system APIs - - ns = system_namespace() - for fun, name in ns.iter(ns.getters): - if name in {"win_service_iter", "win_service_get"}: - continue - with self.subTest(name=name): - try: - ret = fun() - except psutil.AccessDenied: - pass - else: - check(ret) - - # --- exception classes - - b = pickle.loads( - pickle.dumps( - psutil.NoSuchProcess(pid=4567, name='name', msg='msg') - ) - ) - assert isinstance(b, psutil.NoSuchProcess) - assert b.pid == 4567 - assert b.name == 'name' - assert b.msg == 'msg' - - b = pickle.loads( - pickle.dumps( - psutil.ZombieProcess(pid=4567, name='name', ppid=42, msg='msg') - ) - ) - assert isinstance(b, psutil.ZombieProcess) - assert b.pid == 4567 - assert b.ppid == 42 - assert b.name == 'name' - assert b.msg == 'msg' - - b = pickle.loads( - pickle.dumps(psutil.AccessDenied(pid=123, name='name', msg='msg')) - ) - assert isinstance(b, psutil.AccessDenied) - assert b.pid == 123 - assert b.name == 'name' - assert b.msg == 'msg' - - b = pickle.loads( - pickle.dumps( - psutil.TimeoutExpired(seconds=33, pid=4567, name='name') - ) - ) - assert isinstance(b, psutil.TimeoutExpired) - assert b.seconds == 33 - assert b.pid == 4567 - assert b.name == 'name' - - def test_ad_on_process_creation(self): - # We are supposed to be able to instantiate Process also in case - # of zombie processes or access denied. - with mock.patch.object( - psutil.Process, '_get_ident', side_effect=psutil.AccessDenied - ) as meth: - psutil.Process() - assert meth.called - - with mock.patch.object( - psutil.Process, '_get_ident', side_effect=psutil.ZombieProcess(1) - ) as meth: - psutil.Process() - assert meth.called - - with mock.patch.object( - psutil.Process, '_get_ident', side_effect=ValueError - ) as meth: - with pytest.raises(ValueError): - psutil.Process() - assert meth.called - - with mock.patch.object( - psutil.Process, '_get_ident', side_effect=psutil.NoSuchProcess(1) - ) as meth: - with self.assertRaises(psutil.NoSuchProcess): - psutil.Process() - assert meth.called - - def test_sanity_version_check(self): - # see: https://github.com/giampaolo/psutil/issues/564 - with mock.patch( - "psutil._psplatform.cext.version", return_value="0.0.0" - ): - with pytest.raises(ImportError) as cm: - reload_module(psutil) - assert "version conflict" in str(cm.value).lower() - - -# =================================================================== -# --- psutil/_common.py utils -# =================================================================== - - -class TestMemoizeDecorator(PsutilTestCase): - def setUp(self): - self.calls = [] - - tearDown = setUp - - def run_against(self, obj, expected_retval=None): - # no args - for _ in range(2): - ret = obj() - assert self.calls == [((), {})] - if expected_retval is not None: - assert ret == expected_retval - # with args - for _ in range(2): - ret = obj(1) - assert self.calls == [((), {}), ((1,), {})] - if expected_retval is not None: - assert ret == expected_retval - # with args + kwargs - for _ in range(2): - ret = obj(1, bar=2) - assert self.calls == [((), {}), ((1,), {}), ((1,), {'bar': 2})] - if expected_retval is not None: - assert ret == expected_retval - # clear cache - assert len(self.calls) == 3 - obj.cache_clear() - ret = obj() - if expected_retval is not None: - assert ret == expected_retval - assert len(self.calls) == 4 - # docstring - assert obj.__doc__ == "My docstring." - - def test_function(self): - @memoize - def foo(*args, **kwargs): - """My docstring.""" - baseclass.calls.append((args, kwargs)) - return 22 - - baseclass = self - self.run_against(foo, expected_retval=22) - - def test_class(self): - @memoize - class Foo: - """My docstring.""" - - def __init__(self, *args, **kwargs): - baseclass.calls.append((args, kwargs)) - - def bar(self): - return 22 - - baseclass = self - self.run_against(Foo, expected_retval=None) - assert Foo().bar() == 22 - - def test_class_singleton(self): - # @memoize can be used against classes to create singletons - @memoize - class Bar: - def __init__(self, *args, **kwargs): - pass - - assert Bar() is Bar() - assert id(Bar()) == id(Bar()) - assert id(Bar(1)) == id(Bar(1)) - assert id(Bar(1, foo=3)) == id(Bar(1, foo=3)) - assert id(Bar(1)) != id(Bar(2)) - - def test_staticmethod(self): - class Foo: - @staticmethod - @memoize - def bar(*args, **kwargs): - """My docstring.""" - baseclass.calls.append((args, kwargs)) - return 22 - - baseclass = self - self.run_against(Foo().bar, expected_retval=22) - - def test_classmethod(self): - class Foo: - @classmethod - @memoize - def bar(cls, *args, **kwargs): - """My docstring.""" - baseclass.calls.append((args, kwargs)) - return 22 - - baseclass = self - self.run_against(Foo().bar, expected_retval=22) - - def test_original(self): - # This was the original test before I made it dynamic to test it - # against different types. Keeping it anyway. - @memoize - def foo(*args, **kwargs): - """Foo docstring.""" - calls.append(None) - return (args, kwargs) - - calls = [] - # no args - for _ in range(2): - ret = foo() - expected = ((), {}) - assert ret == expected - assert len(calls) == 1 - # with args - for _ in range(2): - ret = foo(1) - expected = ((1,), {}) - assert ret == expected - assert len(calls) == 2 - # with args + kwargs - for _ in range(2): - ret = foo(1, bar=2) - expected = ((1,), {'bar': 2}) - assert ret == expected - assert len(calls) == 3 - # clear cache - foo.cache_clear() - ret = foo() - expected = ((), {}) - assert ret == expected - assert len(calls) == 4 - # docstring - assert foo.__doc__ == "Foo docstring." - - -class TestCommonModule(PsutilTestCase): - def test_memoize_when_activated(self): - class Foo: - @memoize_when_activated - def foo(self): - calls.append(None) - - f = Foo() - calls = [] - f.foo() - f.foo() - assert len(calls) == 2 - - # activate - calls = [] - f.foo.cache_activate(f) - f.foo() - f.foo() - assert len(calls) == 1 - - # deactivate - calls = [] - f.foo.cache_deactivate(f) - f.foo() - f.foo() - assert len(calls) == 2 - - def test_parse_environ_block(self): - def k(s): - return s.upper() if WINDOWS else s - - assert parse_environ_block("a=1\0") == {k("a"): "1"} - assert parse_environ_block("a=1\0b=2\0\0") == { - k("a"): "1", - k("b"): "2", - } - assert parse_environ_block("a=1\0b=\0\0") == {k("a"): "1", k("b"): ""} - # ignore everything after \0\0 - assert parse_environ_block("a=1\0b=2\0\0c=3\0") == { - k("a"): "1", - k("b"): "2", - } - # ignore everything that is not an assignment - assert parse_environ_block("xxx\0a=1\0") == {k("a"): "1"} - assert parse_environ_block("a=1\0=b=2\0") == {k("a"): "1"} - # do not fail if the block is incomplete - assert parse_environ_block("a=1\0b=2") == {k("a"): "1"} - - def test_supports_ipv6(self): - self.addCleanup(supports_ipv6.cache_clear) - if supports_ipv6(): - with mock.patch('psutil._common.socket') as s: - s.has_ipv6 = False - supports_ipv6.cache_clear() - assert not supports_ipv6() - - supports_ipv6.cache_clear() - with mock.patch( - 'psutil._common.socket.socket', side_effect=OSError - ) as s: - assert not supports_ipv6() - assert s.called - - supports_ipv6.cache_clear() - with mock.patch( - 'psutil._common.socket.socket', side_effect=socket.gaierror - ) as s: - assert not supports_ipv6() - supports_ipv6.cache_clear() - assert s.called - - supports_ipv6.cache_clear() - with mock.patch( - 'psutil._common.socket.socket.bind', - side_effect=socket.gaierror, - ) as s: - assert not supports_ipv6() - supports_ipv6.cache_clear() - assert s.called - else: - with pytest.raises(OSError): - sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) - try: - sock.bind(("::1", 0)) - finally: - sock.close() - - def test_isfile_strict(self): - this_file = os.path.abspath(__file__) - assert isfile_strict(this_file) - assert not isfile_strict(os.path.dirname(this_file)) - with mock.patch('psutil._common.os.stat', side_effect=PermissionError): - with pytest.raises(OSError): - isfile_strict(this_file) - with mock.patch( - 'psutil._common.os.stat', side_effect=FileNotFoundError - ): - assert not isfile_strict(this_file) - with mock.patch('psutil._common.stat.S_ISREG', return_value=False): - assert not isfile_strict(this_file) - - def test_debug(self): - with mock.patch.object(psutil._common, "PSUTIL_DEBUG", True): - with contextlib.redirect_stderr(io.StringIO()) as f: - debug("hello") - sys.stderr.flush() - msg = f.getvalue() - assert msg.startswith("psutil-debug"), msg - assert "hello" in msg - assert __file__.replace('.pyc', '.py') in msg - - # supposed to use repr(exc) - with mock.patch.object(psutil._common, "PSUTIL_DEBUG", True): - with contextlib.redirect_stderr(io.StringIO()) as f: - debug(ValueError("this is an error")) - msg = f.getvalue() - assert "ignoring ValueError" in msg - assert "'this is an error'" in msg - - # supposed to use str(exc), because of extra info about file name - with mock.patch.object(psutil._common, "PSUTIL_DEBUG", True): - with contextlib.redirect_stderr(io.StringIO()) as f: - exc = OSError(2, "no such file") - exc.filename = "/foo" - debug(exc) - msg = f.getvalue() - assert "no such file" in msg - assert "/foo" in msg - - def test_cat_bcat(self): - testfn = self.get_testfn() - with open(testfn, "w") as f: - f.write("foo") - assert cat(testfn) == "foo" - assert bcat(testfn) == b"foo" - with pytest.raises(FileNotFoundError): - cat(testfn + '-invalid') - with pytest.raises(FileNotFoundError): - bcat(testfn + '-invalid') - assert cat(testfn + '-invalid', fallback="bar") == "bar" - assert bcat(testfn + '-invalid', fallback="bar") == "bar" - - -# =================================================================== -# --- Tests for wrap_numbers() function. -# =================================================================== - - -nt = collections.namedtuple('foo', 'a b c') - - -class TestWrapNumbers(PsutilTestCase): - def setUp(self): - wrap_numbers.cache_clear() - - tearDown = setUp - - def test_first_call(self): - input = {'disk1': nt(5, 5, 5)} - assert wrap_numbers(input, 'disk_io') == input - - def test_input_hasnt_changed(self): - input = {'disk1': nt(5, 5, 5)} - assert wrap_numbers(input, 'disk_io') == input - assert wrap_numbers(input, 'disk_io') == input - - def test_increase_but_no_wrap(self): - input = {'disk1': nt(5, 5, 5)} - assert wrap_numbers(input, 'disk_io') == input - input = {'disk1': nt(10, 15, 20)} - assert wrap_numbers(input, 'disk_io') == input - input = {'disk1': nt(20, 25, 30)} - assert wrap_numbers(input, 'disk_io') == input - input = {'disk1': nt(20, 25, 30)} - assert wrap_numbers(input, 'disk_io') == input - - def test_wrap(self): - # let's say 100 is the threshold - input = {'disk1': nt(100, 100, 100)} - assert wrap_numbers(input, 'disk_io') == input - # first wrap restarts from 10 - input = {'disk1': nt(100, 100, 10)} - assert wrap_numbers(input, 'disk_io') == {'disk1': nt(100, 100, 110)} - # then it remains the same - input = {'disk1': nt(100, 100, 10)} - assert wrap_numbers(input, 'disk_io') == {'disk1': nt(100, 100, 110)} - # then it goes up - input = {'disk1': nt(100, 100, 90)} - assert wrap_numbers(input, 'disk_io') == {'disk1': nt(100, 100, 190)} - # then it wraps again - input = {'disk1': nt(100, 100, 20)} - assert wrap_numbers(input, 'disk_io') == {'disk1': nt(100, 100, 210)} - # and remains the same - input = {'disk1': nt(100, 100, 20)} - assert wrap_numbers(input, 'disk_io') == {'disk1': nt(100, 100, 210)} - # now wrap another num - input = {'disk1': nt(50, 100, 20)} - assert wrap_numbers(input, 'disk_io') == {'disk1': nt(150, 100, 210)} - # and again - input = {'disk1': nt(40, 100, 20)} - assert wrap_numbers(input, 'disk_io') == {'disk1': nt(190, 100, 210)} - # keep it the same - input = {'disk1': nt(40, 100, 20)} - assert wrap_numbers(input, 'disk_io') == {'disk1': nt(190, 100, 210)} - - def test_changing_keys(self): - # Emulate a case where the second call to disk_io() - # (or whatever) provides a new disk, then the new disk - # disappears on the third call. - input = {'disk1': nt(5, 5, 5)} - assert wrap_numbers(input, 'disk_io') == input - input = {'disk1': nt(5, 5, 5), 'disk2': nt(7, 7, 7)} - assert wrap_numbers(input, 'disk_io') == input - input = {'disk1': nt(8, 8, 8)} - assert wrap_numbers(input, 'disk_io') == input - - def test_changing_keys_w_wrap(self): - input = {'disk1': nt(50, 50, 50), 'disk2': nt(100, 100, 100)} - assert wrap_numbers(input, 'disk_io') == input - # disk 2 wraps - input = {'disk1': nt(50, 50, 50), 'disk2': nt(100, 100, 10)} - assert wrap_numbers(input, 'disk_io') == { - 'disk1': nt(50, 50, 50), - 'disk2': nt(100, 100, 110), - } - # disk 2 disappears - input = {'disk1': nt(50, 50, 50)} - assert wrap_numbers(input, 'disk_io') == input - - # then it appears again; the old wrap is supposed to be - # gone. - input = {'disk1': nt(50, 50, 50), 'disk2': nt(100, 100, 100)} - assert wrap_numbers(input, 'disk_io') == input - # remains the same - input = {'disk1': nt(50, 50, 50), 'disk2': nt(100, 100, 100)} - assert wrap_numbers(input, 'disk_io') == input - # and then wraps again - input = {'disk1': nt(50, 50, 50), 'disk2': nt(100, 100, 10)} - assert wrap_numbers(input, 'disk_io') == { - 'disk1': nt(50, 50, 50), - 'disk2': nt(100, 100, 110), - } - - def test_real_data(self): - d = { - 'nvme0n1': (300, 508, 640, 1571, 5970, 1987, 2049, 451751, 47048), - 'nvme0n1p1': (1171, 2, 5600256, 1024, 516, 0, 0, 0, 8), - 'nvme0n1p2': (54, 54, 2396160, 5165056, 4, 24, 30, 1207, 28), - 'nvme0n1p3': (2389, 4539, 5154, 150, 4828, 1844, 2019, 398, 348), - } - assert wrap_numbers(d, 'disk_io') == d - assert wrap_numbers(d, 'disk_io') == d - # decrease this ↓ - d = { - 'nvme0n1': (100, 508, 640, 1571, 5970, 1987, 2049, 451751, 47048), - 'nvme0n1p1': (1171, 2, 5600256, 1024, 516, 0, 0, 0, 8), - 'nvme0n1p2': (54, 54, 2396160, 5165056, 4, 24, 30, 1207, 28), - 'nvme0n1p3': (2389, 4539, 5154, 150, 4828, 1844, 2019, 398, 348), - } - out = wrap_numbers(d, 'disk_io') - assert out['nvme0n1'][0] == 400 - - # --- cache tests - - def test_cache_first_call(self): - input = {'disk1': nt(5, 5, 5)} - wrap_numbers(input, 'disk_io') - cache = wrap_numbers.cache_info() - assert cache[0] == {'disk_io': input} - assert cache[1] == {'disk_io': {}} - assert cache[2] == {'disk_io': {}} - - def test_cache_call_twice(self): - input = {'disk1': nt(5, 5, 5)} - wrap_numbers(input, 'disk_io') - input = {'disk1': nt(10, 10, 10)} - wrap_numbers(input, 'disk_io') - cache = wrap_numbers.cache_info() - assert cache[0] == {'disk_io': input} - assert cache[1] == { - 'disk_io': {('disk1', 0): 0, ('disk1', 1): 0, ('disk1', 2): 0} - } - assert cache[2] == {'disk_io': {}} - - def test_cache_wrap(self): - # let's say 100 is the threshold - input = {'disk1': nt(100, 100, 100)} - wrap_numbers(input, 'disk_io') - - # first wrap restarts from 10 - input = {'disk1': nt(100, 100, 10)} - wrap_numbers(input, 'disk_io') - cache = wrap_numbers.cache_info() - assert cache[0] == {'disk_io': input} - assert cache[1] == { - 'disk_io': {('disk1', 0): 0, ('disk1', 1): 0, ('disk1', 2): 100} - } - assert cache[2] == {'disk_io': {'disk1': {('disk1', 2)}}} - - def check_cache_info(): - cache = wrap_numbers.cache_info() - assert cache[1] == { - 'disk_io': { - ('disk1', 0): 0, - ('disk1', 1): 0, - ('disk1', 2): 100, - } - } - assert cache[2] == {'disk_io': {'disk1': {('disk1', 2)}}} - - # then it remains the same - input = {'disk1': nt(100, 100, 10)} - wrap_numbers(input, 'disk_io') - cache = wrap_numbers.cache_info() - assert cache[0] == {'disk_io': input} - check_cache_info() - - # then it goes up - input = {'disk1': nt(100, 100, 90)} - wrap_numbers(input, 'disk_io') - cache = wrap_numbers.cache_info() - assert cache[0] == {'disk_io': input} - check_cache_info() - - # then it wraps again - input = {'disk1': nt(100, 100, 20)} - wrap_numbers(input, 'disk_io') - cache = wrap_numbers.cache_info() - assert cache[0] == {'disk_io': input} - assert cache[1] == { - 'disk_io': {('disk1', 0): 0, ('disk1', 1): 0, ('disk1', 2): 190} - } - assert cache[2] == {'disk_io': {'disk1': {('disk1', 2)}}} - - def test_cache_changing_keys(self): - input = {'disk1': nt(5, 5, 5)} - wrap_numbers(input, 'disk_io') - input = {'disk1': nt(5, 5, 5), 'disk2': nt(7, 7, 7)} - wrap_numbers(input, 'disk_io') - cache = wrap_numbers.cache_info() - assert cache[0] == {'disk_io': input} - assert cache[1] == { - 'disk_io': {('disk1', 0): 0, ('disk1', 1): 0, ('disk1', 2): 0} - } - assert cache[2] == {'disk_io': {}} - - def test_cache_clear(self): - input = {'disk1': nt(5, 5, 5)} - wrap_numbers(input, 'disk_io') - wrap_numbers(input, 'disk_io') - wrap_numbers.cache_clear('disk_io') - assert wrap_numbers.cache_info() == ({}, {}, {}) - wrap_numbers.cache_clear('disk_io') - wrap_numbers.cache_clear('?!?') - - @pytest.mark.skipif(not HAS_NET_IO_COUNTERS, reason="not supported") - def test_cache_clear_public_apis(self): - if not psutil.disk_io_counters() or not psutil.net_io_counters(): - raise pytest.skip("no disks or NICs available") - psutil.disk_io_counters() - psutil.net_io_counters() - caches = wrap_numbers.cache_info() - for cache in caches: - assert 'psutil.disk_io_counters' in cache - assert 'psutil.net_io_counters' in cache - - psutil.disk_io_counters.cache_clear() - caches = wrap_numbers.cache_info() - for cache in caches: - assert 'psutil.net_io_counters' in cache - assert 'psutil.disk_io_counters' not in cache - - psutil.net_io_counters.cache_clear() - caches = wrap_numbers.cache_info() - assert caches == ({}, {}, {}) diff --git a/PortablePython/Lib/site-packages/psutil/tests/test_osx.py b/PortablePython/Lib/site-packages/psutil/tests/test_osx.py deleted file mode 100644 index 050418c..0000000 --- a/PortablePython/Lib/site-packages/psutil/tests/test_osx.py +++ /dev/null @@ -1,197 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -"""macOS specific tests.""" - -import platform -import re -import time - -import psutil -from psutil import MACOS -from psutil import POSIX -from psutil.tests import CI_TESTING -from psutil.tests import HAS_BATTERY -from psutil.tests import TOLERANCE_DISK_USAGE -from psutil.tests import TOLERANCE_SYS_MEM -from psutil.tests import PsutilTestCase -from psutil.tests import pytest -from psutil.tests import retry_on_failure -from psutil.tests import sh -from psutil.tests import spawn_testproc -from psutil.tests import terminate - - -if POSIX: - from psutil._psutil_posix import getpagesize - - -def sysctl(cmdline): - """Expects a sysctl command with an argument and parse the result - returning only the value of interest. - """ - out = sh(cmdline) - result = out.split()[1] - try: - return int(result) - except ValueError: - return result - - -def vm_stat(field): - """Wrapper around 'vm_stat' cmdline utility.""" - out = sh('vm_stat') - for line in out.split('\n'): - if field in line: - break - else: - raise ValueError("line not found") - return int(re.search(r'\d+', line).group(0)) * getpagesize() - - -@pytest.mark.skipif(not MACOS, reason="MACOS only") -class TestProcess(PsutilTestCase): - @classmethod - def setUpClass(cls): - cls.pid = spawn_testproc().pid - - @classmethod - def tearDownClass(cls): - terminate(cls.pid) - - def test_process_create_time(self): - output = sh(f"ps -o lstart -p {self.pid}") - start_ps = output.replace('STARTED', '').strip() - hhmmss = start_ps.split(' ')[-2] - year = start_ps.split(' ')[-1] - start_psutil = psutil.Process(self.pid).create_time() - assert hhmmss == time.strftime( - "%H:%M:%S", time.localtime(start_psutil) - ) - assert year == time.strftime("%Y", time.localtime(start_psutil)) - - -@pytest.mark.skipif(not MACOS, reason="MACOS only") -class TestSystemAPIs(PsutilTestCase): - - # --- disk - - @retry_on_failure() - def test_disks(self): - # test psutil.disk_usage() and psutil.disk_partitions() - # against "df -a" - def df(path): - out = sh(f'df -k "{path}"').strip() - lines = out.split('\n') - lines.pop(0) - line = lines.pop(0) - dev, total, used, free = line.split()[:4] - if dev == 'none': - dev = '' - total = int(total) * 1024 - used = int(used) * 1024 - free = int(free) * 1024 - return dev, total, used, free - - for part in psutil.disk_partitions(all=False): - usage = psutil.disk_usage(part.mountpoint) - dev, total, used, free = df(part.mountpoint) - assert part.device == dev - assert usage.total == total - assert abs(usage.free - free) < TOLERANCE_DISK_USAGE - assert abs(usage.used - used) < TOLERANCE_DISK_USAGE - - # --- cpu - - def test_cpu_count_logical(self): - num = sysctl("sysctl hw.logicalcpu") - assert num == psutil.cpu_count(logical=True) - - def test_cpu_count_cores(self): - num = sysctl("sysctl hw.physicalcpu") - assert num == psutil.cpu_count(logical=False) - - # TODO: remove this once 1892 is fixed - @pytest.mark.skipif( - MACOS and platform.machine() == 'arm64', reason="skipped due to #1892" - ) - def test_cpu_freq(self): - freq = psutil.cpu_freq() - assert freq.current * 1000 * 1000 == sysctl("sysctl hw.cpufrequency") - assert freq.min * 1000 * 1000 == sysctl("sysctl hw.cpufrequency_min") - assert freq.max * 1000 * 1000 == sysctl("sysctl hw.cpufrequency_max") - - # --- virtual mem - - def test_vmem_total(self): - sysctl_hwphymem = sysctl('sysctl hw.memsize') - assert sysctl_hwphymem == psutil.virtual_memory().total - - @pytest.mark.skipif( - CI_TESTING and MACOS and platform.machine() == 'arm64', - reason="skipped on MACOS + ARM64 + CI_TESTING", - ) - @retry_on_failure() - def test_vmem_free(self): - vmstat_val = vm_stat("free") - psutil_val = psutil.virtual_memory().free - assert abs(psutil_val - vmstat_val) < TOLERANCE_SYS_MEM - - @retry_on_failure() - def test_vmem_active(self): - vmstat_val = vm_stat("active") - psutil_val = psutil.virtual_memory().active - assert abs(psutil_val - vmstat_val) < TOLERANCE_SYS_MEM - - @retry_on_failure() - def test_vmem_inactive(self): - vmstat_val = vm_stat("inactive") - psutil_val = psutil.virtual_memory().inactive - assert abs(psutil_val - vmstat_val) < TOLERANCE_SYS_MEM - - @retry_on_failure() - def test_vmem_wired(self): - vmstat_val = vm_stat("wired") - psutil_val = psutil.virtual_memory().wired - assert abs(psutil_val - vmstat_val) < TOLERANCE_SYS_MEM - - # --- swap mem - - @retry_on_failure() - def test_swapmem_sin(self): - vmstat_val = vm_stat("Pageins") - psutil_val = psutil.swap_memory().sin - assert abs(psutil_val - vmstat_val) < TOLERANCE_SYS_MEM - - @retry_on_failure() - def test_swapmem_sout(self): - vmstat_val = vm_stat("Pageout") - psutil_val = psutil.swap_memory().sout - assert abs(psutil_val - vmstat_val) < TOLERANCE_SYS_MEM - - # --- network - - def test_net_if_stats(self): - for name, stats in psutil.net_if_stats().items(): - try: - out = sh(f"ifconfig {name}") - except RuntimeError: - pass - else: - assert stats.isup == ('RUNNING' in out), out - assert stats.mtu == int(re.findall(r'mtu (\d+)', out)[0]) - - # --- sensors_battery - - @pytest.mark.skipif(not HAS_BATTERY, reason="no battery") - def test_sensors_battery(self): - out = sh("pmset -g batt") - percent = re.search(r"(\d+)%", out).group(1) - drawing_from = re.search(r"Now drawing from '([^']+)'", out).group(1) - power_plugged = drawing_from == "AC Power" - psutil_result = psutil.sensors_battery() - assert psutil_result.power_plugged == power_plugged - assert psutil_result.percent == int(percent) diff --git a/PortablePython/Lib/site-packages/psutil/tests/test_posix.py b/PortablePython/Lib/site-packages/psutil/tests/test_posix.py deleted file mode 100644 index a784492..0000000 --- a/PortablePython/Lib/site-packages/psutil/tests/test_posix.py +++ /dev/null @@ -1,488 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -"""POSIX specific tests.""" - -import datetime -import errno -import os -import re -import shutil -import subprocess -import time -from unittest import mock - -import psutil -from psutil import AIX -from psutil import BSD -from psutil import LINUX -from psutil import MACOS -from psutil import OPENBSD -from psutil import POSIX -from psutil import SUNOS -from psutil.tests import AARCH64 -from psutil.tests import HAS_NET_IO_COUNTERS -from psutil.tests import PYTHON_EXE -from psutil.tests import PsutilTestCase -from psutil.tests import pytest -from psutil.tests import retry_on_failure -from psutil.tests import sh -from psutil.tests import skip_on_access_denied -from psutil.tests import spawn_testproc -from psutil.tests import terminate - - -if POSIX: - import mmap - import resource - - from psutil._psutil_posix import getpagesize - - -def ps(fmt, pid=None): - """Wrapper for calling the ps command with a little bit of cross-platform - support for a narrow range of features. - """ - - cmd = ['ps'] - - if LINUX: - cmd.append('--no-headers') - - if pid is not None: - cmd.extend(['-p', str(pid)]) - elif SUNOS or AIX: - cmd.append('-A') - else: - cmd.append('ax') - - if SUNOS: - fmt = fmt.replace("start", "stime") - - cmd.extend(['-o', fmt]) - - output = sh(cmd) - - output = output.splitlines() if LINUX else output.splitlines()[1:] - - all_output = [] - for line in output: - line = line.strip() - - try: - line = int(line) - except ValueError: - pass - - all_output.append(line) - - if pid is None: - return all_output - else: - return all_output[0] - - -# ps "-o" field names differ wildly between platforms. -# "comm" means "only executable name" but is not available on BSD platforms. -# "args" means "command with all its arguments", and is also not available -# on BSD platforms. -# "command" is like "args" on most platforms, but like "comm" on AIX, -# and not available on SUNOS. -# so for the executable name we can use "comm" on Solaris and split "command" -# on other platforms. -# to get the cmdline (with args) we have to use "args" on AIX and -# Solaris, and can use "command" on all others. - - -def ps_name(pid): - field = "command" - if SUNOS: - field = "comm" - command = ps(field, pid).split() - return command[0] - - -def ps_args(pid): - field = "command" - if AIX or SUNOS: - field = "args" - out = ps(field, pid) - # observed on BSD + Github CI: '/usr/local/bin/python3 -E -O (python3.9)' - out = re.sub(r"\(python.*?\)$", "", out) - return out.strip() - - -def ps_rss(pid): - field = "rss" - if AIX: - field = "rssize" - return ps(field, pid) - - -def ps_vsz(pid): - field = "vsz" - if AIX: - field = "vsize" - return ps(field, pid) - - -def df(device): - try: - out = sh(f"df -k {device}").strip() - except RuntimeError as err: - if "device busy" in str(err).lower(): - raise pytest.skip("df returned EBUSY") - raise - line = out.split('\n')[1] - fields = line.split() - sys_total = int(fields[1]) * 1024 - sys_used = int(fields[2]) * 1024 - sys_free = int(fields[3]) * 1024 - sys_percent = float(fields[4].replace('%', '')) - return (sys_total, sys_used, sys_free, sys_percent) - - -@pytest.mark.skipif(not POSIX, reason="POSIX only") -class TestProcess(PsutilTestCase): - """Compare psutil results against 'ps' command line utility (mainly).""" - - @classmethod - def setUpClass(cls): - cls.pid = spawn_testproc( - [PYTHON_EXE, "-E", "-O"], stdin=subprocess.PIPE - ).pid - - @classmethod - def tearDownClass(cls): - terminate(cls.pid) - - def test_ppid(self): - ppid_ps = ps('ppid', self.pid) - ppid_psutil = psutil.Process(self.pid).ppid() - assert ppid_ps == ppid_psutil - - def test_uid(self): - uid_ps = ps('uid', self.pid) - uid_psutil = psutil.Process(self.pid).uids().real - assert uid_ps == uid_psutil - - def test_gid(self): - gid_ps = ps('rgid', self.pid) - gid_psutil = psutil.Process(self.pid).gids().real - assert gid_ps == gid_psutil - - def test_username(self): - username_ps = ps('user', self.pid) - username_psutil = psutil.Process(self.pid).username() - assert username_ps == username_psutil - - def test_username_no_resolution(self): - # Emulate a case where the system can't resolve the uid to - # a username in which case psutil is supposed to return - # the stringified uid. - p = psutil.Process() - with mock.patch("psutil.pwd.getpwuid", side_effect=KeyError) as fun: - assert p.username() == str(p.uids().real) - assert fun.called - - @skip_on_access_denied() - @retry_on_failure() - def test_rss_memory(self): - # give python interpreter some time to properly initialize - # so that the results are the same - time.sleep(0.1) - rss_ps = ps_rss(self.pid) - rss_psutil = psutil.Process(self.pid).memory_info()[0] / 1024 - assert rss_ps == rss_psutil - - @skip_on_access_denied() - @retry_on_failure() - def test_vsz_memory(self): - # give python interpreter some time to properly initialize - # so that the results are the same - time.sleep(0.1) - vsz_ps = ps_vsz(self.pid) - vsz_psutil = psutil.Process(self.pid).memory_info()[1] / 1024 - assert vsz_ps == vsz_psutil - - def test_name(self): - name_ps = ps_name(self.pid) - # remove path if there is any, from the command - name_ps = os.path.basename(name_ps).lower() - name_psutil = psutil.Process(self.pid).name().lower() - # ...because of how we calculate PYTHON_EXE; on MACOS this may - # be "pythonX.Y". - name_ps = re.sub(r"\d.\d", "", name_ps) - name_psutil = re.sub(r"\d.\d", "", name_psutil) - # ...may also be "python.X" - name_ps = re.sub(r"\d", "", name_ps) - name_psutil = re.sub(r"\d", "", name_psutil) - assert name_ps == name_psutil - - def test_name_long(self): - # On UNIX the kernel truncates the name to the first 15 - # characters. In such a case psutil tries to determine the - # full name from the cmdline. - name = "long-program-name" - cmdline = ["long-program-name-extended", "foo", "bar"] - with mock.patch("psutil._psplatform.Process.name", return_value=name): - with mock.patch( - "psutil._psplatform.Process.cmdline", return_value=cmdline - ): - p = psutil.Process() - assert p.name() == "long-program-name-extended" - - def test_name_long_cmdline_ad_exc(self): - # Same as above but emulates a case where cmdline() raises - # AccessDenied in which case psutil is supposed to return - # the truncated name instead of crashing. - name = "long-program-name" - with mock.patch("psutil._psplatform.Process.name", return_value=name): - with mock.patch( - "psutil._psplatform.Process.cmdline", - side_effect=psutil.AccessDenied(0, ""), - ): - p = psutil.Process() - assert p.name() == "long-program-name" - - def test_name_long_cmdline_nsp_exc(self): - # Same as above but emulates a case where cmdline() raises NSP - # which is supposed to propagate. - name = "long-program-name" - with mock.patch("psutil._psplatform.Process.name", return_value=name): - with mock.patch( - "psutil._psplatform.Process.cmdline", - side_effect=psutil.NoSuchProcess(0, ""), - ): - p = psutil.Process() - with pytest.raises(psutil.NoSuchProcess): - p.name() - - @pytest.mark.skipif(MACOS or BSD, reason="ps -o start not available") - def test_create_time(self): - time_ps = ps('start', self.pid) - time_psutil = psutil.Process(self.pid).create_time() - time_psutil_tstamp = datetime.datetime.fromtimestamp( - time_psutil - ).strftime("%H:%M:%S") - # sometimes ps shows the time rounded up instead of down, so we check - # for both possible values - round_time_psutil = round(time_psutil) - round_time_psutil_tstamp = datetime.datetime.fromtimestamp( - round_time_psutil - ).strftime("%H:%M:%S") - assert time_ps in {time_psutil_tstamp, round_time_psutil_tstamp} - - def test_exe(self): - ps_pathname = ps_name(self.pid) - psutil_pathname = psutil.Process(self.pid).exe() - try: - assert ps_pathname == psutil_pathname - except AssertionError: - # certain platforms such as BSD are more accurate returning: - # "/usr/local/bin/python3.7" - # ...instead of: - # "/usr/local/bin/python" - # We do not want to consider this difference in accuracy - # an error. - adjusted_ps_pathname = ps_pathname[: len(ps_pathname)] - assert ps_pathname == adjusted_ps_pathname - - # On macOS the official python installer exposes a python wrapper that - # executes a python executable hidden inside an application bundle inside - # the Python framework. - # There's a race condition between the ps call & the psutil call below - # depending on the completion of the execve call so let's retry on failure - @retry_on_failure() - def test_cmdline(self): - ps_cmdline = ps_args(self.pid) - psutil_cmdline = " ".join(psutil.Process(self.pid).cmdline()) - if AARCH64 and len(ps_cmdline) < len(psutil_cmdline): - assert psutil_cmdline.startswith(ps_cmdline) - else: - assert ps_cmdline == psutil_cmdline - - # On SUNOS "ps" reads niceness /proc/pid/psinfo which returns an - # incorrect value (20); the real deal is getpriority(2) which - # returns 0; psutil relies on it, see: - # https://github.com/giampaolo/psutil/issues/1082 - # AIX has the same issue - @pytest.mark.skipif(SUNOS, reason="not reliable on SUNOS") - @pytest.mark.skipif(AIX, reason="not reliable on AIX") - def test_nice(self): - ps_nice = ps('nice', self.pid) - psutil_nice = psutil.Process().nice() - assert ps_nice == psutil_nice - - -@pytest.mark.skipif(not POSIX, reason="POSIX only") -class TestSystemAPIs(PsutilTestCase): - """Test some system APIs.""" - - @retry_on_failure() - def test_pids(self): - # Note: this test might fail if the OS is starting/killing - # other processes in the meantime - pids_ps = sorted(ps("pid")) - pids_psutil = psutil.pids() - - # on MACOS and OPENBSD ps doesn't show pid 0 - if MACOS or (OPENBSD and 0 not in pids_ps): - pids_ps.insert(0, 0) - - # There will often be one more process in pids_ps for ps itself - if len(pids_ps) - len(pids_psutil) > 1: - difference = [x for x in pids_psutil if x not in pids_ps] + [ - x for x in pids_ps if x not in pids_psutil - ] - raise self.fail("difference: " + str(difference)) - - # for some reason ifconfig -a does not report all interfaces - # returned by psutil - @pytest.mark.skipif(SUNOS, reason="unreliable on SUNOS") - @pytest.mark.skipif(not shutil.which("ifconfig"), reason="no ifconfig cmd") - @pytest.mark.skipif(not HAS_NET_IO_COUNTERS, reason="not supported") - def test_nic_names(self): - output = sh("ifconfig -a") - for nic in psutil.net_io_counters(pernic=True): - for line in output.split(): - if line.startswith(nic): - break - else: - raise self.fail( - f"couldn't find {nic} nic in 'ifconfig -a'" - f" output\n{output}" - ) - - # @pytest.mark.skipif(CI_TESTING and not psutil.users(), - # reason="unreliable on CI") - @retry_on_failure() - def test_users(self): - out = sh("who -u") - if not out.strip(): - raise pytest.skip("no users on this system") - lines = out.split('\n') - users = [x.split()[0] for x in lines] - terminals = [x.split()[1] for x in lines] - assert len(users) == len(psutil.users()) - with self.subTest(psutil=psutil.users(), who=out): - for idx, u in enumerate(psutil.users()): - assert u.name == users[idx] - assert u.terminal == terminals[idx] - if u.pid is not None: # None on OpenBSD - psutil.Process(u.pid) - - @retry_on_failure() - def test_users_started(self): - out = sh("who -u") - if not out.strip(): - raise pytest.skip("no users on this system") - tstamp = None - # '2023-04-11 09:31' (Linux) - started = re.findall(r"\d\d\d\d-\d\d-\d\d \d\d:\d\d", out) - if started: - tstamp = "%Y-%m-%d %H:%M" - else: - # 'Apr 10 22:27' (macOS) - started = re.findall(r"[A-Z][a-z][a-z] \d\d \d\d:\d\d", out) - if started: - tstamp = "%b %d %H:%M" - else: - # 'Apr 10' - started = re.findall(r"[A-Z][a-z][a-z] \d\d", out) - if started: - tstamp = "%b %d" - else: - # 'apr 10' (sunOS) - started = re.findall(r"[a-z][a-z][a-z] \d\d", out) - if started: - tstamp = "%b %d" - started = [x.capitalize() for x in started] - - if not tstamp: - raise pytest.skip(f"cannot interpret tstamp in who output\n{out}") - - with self.subTest(psutil=psutil.users(), who=out): - for idx, u in enumerate(psutil.users()): - psutil_value = datetime.datetime.fromtimestamp( - u.started - ).strftime(tstamp) - assert psutil_value == started[idx] - - def test_pid_exists_let_raise(self): - # According to "man 2 kill" possible error values for kill - # are (EINVAL, EPERM, ESRCH). Test that any other errno - # results in an exception. - with mock.patch( - "psutil._psposix.os.kill", side_effect=OSError(errno.EBADF, "") - ) as m: - with pytest.raises(OSError): - psutil._psposix.pid_exists(os.getpid()) - assert m.called - - def test_os_waitpid_let_raise(self): - # os.waitpid() is supposed to catch EINTR and ECHILD only. - # Test that any other errno results in an exception. - with mock.patch( - "psutil._psposix.os.waitpid", side_effect=OSError(errno.EBADF, "") - ) as m: - with pytest.raises(OSError): - psutil._psposix.wait_pid(os.getpid()) - assert m.called - - def test_os_waitpid_eintr(self): - # os.waitpid() is supposed to "retry" on EINTR. - with mock.patch( - "psutil._psposix.os.waitpid", side_effect=OSError(errno.EINTR, "") - ) as m: - with pytest.raises(psutil._psposix.TimeoutExpired): - psutil._psposix.wait_pid(os.getpid(), timeout=0.01) - assert m.called - - def test_os_waitpid_bad_ret_status(self): - # Simulate os.waitpid() returning a bad status. - with mock.patch( - "psutil._psposix.os.waitpid", return_value=(1, -1) - ) as m: - with pytest.raises(ValueError): - psutil._psposix.wait_pid(os.getpid()) - assert m.called - - # AIX can return '-' in df output instead of numbers, e.g. for /proc - @pytest.mark.skipif(AIX, reason="unreliable on AIX") - @retry_on_failure() - def test_disk_usage(self): - tolerance = 4 * 1024 * 1024 # 4MB - for part in psutil.disk_partitions(all=False): - usage = psutil.disk_usage(part.mountpoint) - try: - sys_total, sys_used, sys_free, sys_percent = df(part.device) - except RuntimeError as err: - # see: - # https://travis-ci.org/giampaolo/psutil/jobs/138338464 - # https://travis-ci.org/giampaolo/psutil/jobs/138343361 - err = str(err).lower() - if ( - "no such file or directory" in err - or "raw devices not supported" in err - or "permission denied" in err - ): - continue - raise - else: - assert abs(usage.total - sys_total) < tolerance - assert abs(usage.used - sys_used) < tolerance - assert abs(usage.free - sys_free) < tolerance - assert abs(usage.percent - sys_percent) <= 1 - - -@pytest.mark.skipif(not POSIX, reason="POSIX only") -class TestMisc(PsutilTestCase): - def test_getpagesize(self): - pagesize = getpagesize() - assert pagesize > 0 - assert pagesize == resource.getpagesize() - assert pagesize == mmap.PAGESIZE diff --git a/PortablePython/Lib/site-packages/psutil/tests/test_process.py b/PortablePython/Lib/site-packages/psutil/tests/test_process.py deleted file mode 100644 index 9ba1ba0..0000000 --- a/PortablePython/Lib/site-packages/psutil/tests/test_process.py +++ /dev/null @@ -1,1667 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -"""Tests for psutil.Process class.""" - -import collections -import contextlib -import errno -import getpass -import io -import itertools -import os -import signal -import socket -import stat -import string -import subprocess -import sys -import textwrap -import time -from unittest import mock - -import psutil -from psutil import AIX -from psutil import BSD -from psutil import LINUX -from psutil import MACOS -from psutil import NETBSD -from psutil import OPENBSD -from psutil import OSX -from psutil import POSIX -from psutil import WINDOWS -from psutil._common import open_text -from psutil.tests import CI_TESTING -from psutil.tests import GITHUB_ACTIONS -from psutil.tests import GLOBAL_TIMEOUT -from psutil.tests import HAS_CPU_AFFINITY -from psutil.tests import HAS_ENVIRON -from psutil.tests import HAS_IONICE -from psutil.tests import HAS_MEMORY_MAPS -from psutil.tests import HAS_PROC_CPU_NUM -from psutil.tests import HAS_PROC_IO_COUNTERS -from psutil.tests import HAS_RLIMIT -from psutil.tests import HAS_THREADS -from psutil.tests import MACOS_11PLUS -from psutil.tests import PYPY -from psutil.tests import PYTHON_EXE -from psutil.tests import PYTHON_EXE_ENV -from psutil.tests import PsutilTestCase -from psutil.tests import ThreadTask -from psutil.tests import call_until -from psutil.tests import copyload_shared_lib -from psutil.tests import create_c_exe -from psutil.tests import create_py_exe -from psutil.tests import process_namespace -from psutil.tests import pytest -from psutil.tests import reap_children -from psutil.tests import retry_on_failure -from psutil.tests import sh -from psutil.tests import skip_on_access_denied -from psutil.tests import skip_on_not_implemented -from psutil.tests import wait_for_pid - - -# =================================================================== -# --- psutil.Process class tests -# =================================================================== - - -class TestProcess(PsutilTestCase): - """Tests for psutil.Process class.""" - - def spawn_psproc(self, *args, **kwargs): - sproc = self.spawn_testproc(*args, **kwargs) - try: - return psutil.Process(sproc.pid) - except psutil.NoSuchProcess: - self.assertPidGone(sproc.pid) - raise - - # --- - - def test_pid(self): - p = psutil.Process() - assert p.pid == os.getpid() - with pytest.raises(AttributeError): - p.pid = 33 - - def test_kill(self): - p = self.spawn_psproc() - p.kill() - code = p.wait() - if WINDOWS: - assert code == signal.SIGTERM - else: - assert code == -signal.SIGKILL - self.assertProcessGone(p) - - def test_terminate(self): - p = self.spawn_psproc() - p.terminate() - code = p.wait() - if WINDOWS: - assert code == signal.SIGTERM - else: - assert code == -signal.SIGTERM - self.assertProcessGone(p) - - def test_send_signal(self): - sig = signal.SIGKILL if POSIX else signal.SIGTERM - p = self.spawn_psproc() - p.send_signal(sig) - code = p.wait() - if WINDOWS: - assert code == sig - else: - assert code == -sig - self.assertProcessGone(p) - - @pytest.mark.skipif(not POSIX, reason="not POSIX") - def test_send_signal_mocked(self): - sig = signal.SIGTERM - p = self.spawn_psproc() - with mock.patch('psutil.os.kill', side_effect=ProcessLookupError): - with pytest.raises(psutil.NoSuchProcess): - p.send_signal(sig) - - p = self.spawn_psproc() - with mock.patch('psutil.os.kill', side_effect=PermissionError): - with pytest.raises(psutil.AccessDenied): - p.send_signal(sig) - - def test_wait_exited(self): - # Test waitpid() + WIFEXITED -> WEXITSTATUS. - # normal return, same as exit(0) - cmd = [PYTHON_EXE, "-c", "pass"] - p = self.spawn_psproc(cmd) - code = p.wait() - assert code == 0 - self.assertProcessGone(p) - # exit(1), implicit in case of error - cmd = [PYTHON_EXE, "-c", "1 / 0"] - p = self.spawn_psproc(cmd, stderr=subprocess.PIPE) - code = p.wait() - assert code == 1 - self.assertProcessGone(p) - # via sys.exit() - cmd = [PYTHON_EXE, "-c", "import sys; sys.exit(5);"] - p = self.spawn_psproc(cmd) - code = p.wait() - assert code == 5 - self.assertProcessGone(p) - # via os._exit() - cmd = [PYTHON_EXE, "-c", "import os; os._exit(5);"] - p = self.spawn_psproc(cmd) - code = p.wait() - assert code == 5 - self.assertProcessGone(p) - - @pytest.mark.skipif(NETBSD, reason="fails on NETBSD") - def test_wait_stopped(self): - p = self.spawn_psproc() - if POSIX: - # Test waitpid() + WIFSTOPPED and WIFCONTINUED. - # Note: if a process is stopped it ignores SIGTERM. - p.send_signal(signal.SIGSTOP) - with pytest.raises(psutil.TimeoutExpired): - p.wait(timeout=0.001) - p.send_signal(signal.SIGCONT) - with pytest.raises(psutil.TimeoutExpired): - p.wait(timeout=0.001) - p.send_signal(signal.SIGTERM) - assert p.wait() == -signal.SIGTERM - assert p.wait() == -signal.SIGTERM - else: - p.suspend() - with pytest.raises(psutil.TimeoutExpired): - p.wait(timeout=0.001) - p.resume() - with pytest.raises(psutil.TimeoutExpired): - p.wait(timeout=0.001) - p.terminate() - assert p.wait() == signal.SIGTERM - assert p.wait() == signal.SIGTERM - - def test_wait_non_children(self): - # Test wait() against a process which is not our direct - # child. - child, grandchild = self.spawn_children_pair() - with pytest.raises(psutil.TimeoutExpired): - child.wait(0.01) - with pytest.raises(psutil.TimeoutExpired): - grandchild.wait(0.01) - # We also terminate the direct child otherwise the - # grandchild will hang until the parent is gone. - child.terminate() - grandchild.terminate() - child_ret = child.wait() - grandchild_ret = grandchild.wait() - if POSIX: - assert child_ret == -signal.SIGTERM - # For processes which are not our children we're supposed - # to get None. - assert grandchild_ret is None - else: - assert child_ret == signal.SIGTERM - assert child_ret == signal.SIGTERM - - def test_wait_timeout(self): - p = self.spawn_psproc() - p.name() - with pytest.raises(psutil.TimeoutExpired): - p.wait(0.01) - with pytest.raises(psutil.TimeoutExpired): - p.wait(0) - with pytest.raises(ValueError): - p.wait(-1) - - def test_wait_timeout_nonblocking(self): - p = self.spawn_psproc() - with pytest.raises(psutil.TimeoutExpired): - p.wait(0) - p.kill() - stop_at = time.time() + GLOBAL_TIMEOUT - while time.time() < stop_at: - try: - code = p.wait(0) - break - except psutil.TimeoutExpired: - pass - else: - raise self.fail('timeout') - if POSIX: - assert code == -signal.SIGKILL - else: - assert code == signal.SIGTERM - self.assertProcessGone(p) - - def test_cpu_percent(self): - p = psutil.Process() - p.cpu_percent(interval=0.001) - p.cpu_percent(interval=0.001) - for _ in range(100): - percent = p.cpu_percent(interval=None) - assert isinstance(percent, float) - assert percent >= 0.0 - with pytest.raises(ValueError): - p.cpu_percent(interval=-1) - - def test_cpu_percent_numcpus_none(self): - # See: https://github.com/giampaolo/psutil/issues/1087 - with mock.patch('psutil.cpu_count', return_value=None) as m: - psutil.Process().cpu_percent() - assert m.called - - def test_cpu_times(self): - times = psutil.Process().cpu_times() - assert times.user >= 0.0, times - assert times.system >= 0.0, times - assert times.children_user >= 0.0, times - assert times.children_system >= 0.0, times - if LINUX: - assert times.iowait >= 0.0, times - # make sure returned values can be pretty printed with strftime - for name in times._fields: - time.strftime("%H:%M:%S", time.localtime(getattr(times, name))) - - def test_cpu_times_2(self): - def waste_cpu(): - stop_at = os.times().user + 0.2 - while os.times().user < stop_at: - for x in range(100000): - x **= 2 - - waste_cpu() - a = psutil.Process().cpu_times() - b = os.times() - self.assertAlmostEqual(a.user, b.user, delta=0.1) - self.assertAlmostEqual(a.system, b.system, delta=0.1) - - @pytest.mark.skipif(not HAS_PROC_CPU_NUM, reason="not supported") - def test_cpu_num(self): - p = psutil.Process() - num = p.cpu_num() - assert num >= 0 - if psutil.cpu_count() == 1: - assert num == 0 - assert p.cpu_num() in range(psutil.cpu_count()) - - def test_create_time(self): - p = self.spawn_psproc() - now = time.time() - create_time = p.create_time() - - # Use time.time() as base value to compare our result using a - # tolerance of +/- 1 second. - # It will fail if the difference between the values is > 2s. - difference = abs(create_time - now) - if difference > 2: - raise self.fail( - f"expected: {now}, found: {create_time}, difference:" - f" {difference}" - ) - - # make sure returned value can be pretty printed with strftime - time.strftime("%Y %m %d %H:%M:%S", time.localtime(p.create_time())) - - @pytest.mark.skipif(not POSIX, reason="POSIX only") - def test_terminal(self): - terminal = psutil.Process().terminal() - if terminal is not None: - try: - tty = os.path.realpath(sh('tty')) - except RuntimeError: - # Note: happens if pytest is run without the `-s` opt. - raise pytest.skip("can't rely on `tty` CLI") - else: - assert terminal == tty - - @pytest.mark.skipif(not HAS_PROC_IO_COUNTERS, reason="not supported") - @skip_on_not_implemented(only_if=LINUX) - def test_io_counters(self): - p = psutil.Process() - # test reads - io1 = p.io_counters() - with open(PYTHON_EXE, 'rb') as f: - f.read() - io2 = p.io_counters() - if not BSD and not AIX: - assert io2.read_count > io1.read_count - assert io2.write_count == io1.write_count - if LINUX: - assert io2.read_chars > io1.read_chars - assert io2.write_chars == io1.write_chars - else: - assert io2.read_bytes >= io1.read_bytes - assert io2.write_bytes >= io1.write_bytes - - # test writes - io1 = p.io_counters() - with open(self.get_testfn(), 'wb') as f: - f.write(bytes("x" * 1000000, 'ascii')) - io2 = p.io_counters() - assert io2.write_count >= io1.write_count - assert io2.write_bytes >= io1.write_bytes - assert io2.read_count >= io1.read_count - assert io2.read_bytes >= io1.read_bytes - if LINUX: - assert io2.write_chars > io1.write_chars - assert io2.read_chars >= io1.read_chars - - # sanity check - for i in range(len(io2)): - if BSD and i >= 2: - # On BSD read_bytes and write_bytes are always set to -1. - continue - assert io2[i] >= 0 - assert io2[i] >= 0 - - @pytest.mark.skipif(not HAS_IONICE, reason="not supported") - @pytest.mark.skipif(not LINUX, reason="linux only") - def test_ionice_linux(self): - def cleanup(init): - ioclass, value = init - if ioclass == psutil.IOPRIO_CLASS_NONE: - value = 0 - p.ionice(ioclass, value) - - p = psutil.Process() - if not CI_TESTING: - assert p.ionice()[0] == psutil.IOPRIO_CLASS_NONE - assert psutil.IOPRIO_CLASS_NONE == 0 - assert psutil.IOPRIO_CLASS_RT == 1 # high - assert psutil.IOPRIO_CLASS_BE == 2 # normal - assert psutil.IOPRIO_CLASS_IDLE == 3 # low - init = p.ionice() - self.addCleanup(cleanup, init) - - # low - p.ionice(psutil.IOPRIO_CLASS_IDLE) - assert tuple(p.ionice()) == (psutil.IOPRIO_CLASS_IDLE, 0) - with pytest.raises(ValueError): # accepts no value - p.ionice(psutil.IOPRIO_CLASS_IDLE, value=7) - # normal - p.ionice(psutil.IOPRIO_CLASS_BE) - assert tuple(p.ionice()) == (psutil.IOPRIO_CLASS_BE, 0) - p.ionice(psutil.IOPRIO_CLASS_BE, value=7) - assert tuple(p.ionice()) == (psutil.IOPRIO_CLASS_BE, 7) - with pytest.raises(ValueError): - p.ionice(psutil.IOPRIO_CLASS_BE, value=8) - try: - p.ionice(psutil.IOPRIO_CLASS_RT, value=7) - except psutil.AccessDenied: - pass - # errs - with pytest.raises(ValueError, match="ioclass accepts no value"): - p.ionice(psutil.IOPRIO_CLASS_NONE, 1) - with pytest.raises(ValueError, match="ioclass accepts no value"): - p.ionice(psutil.IOPRIO_CLASS_IDLE, 1) - with pytest.raises( - ValueError, match="'ioclass' argument must be specified" - ): - p.ionice(value=1) - - @pytest.mark.skipif(not HAS_IONICE, reason="not supported") - @pytest.mark.skipif( - not WINDOWS, reason="not supported on this win version" - ) - def test_ionice_win(self): - p = psutil.Process() - if not CI_TESTING: - assert p.ionice() == psutil.IOPRIO_NORMAL - init = p.ionice() - self.addCleanup(p.ionice, init) - - # base - p.ionice(psutil.IOPRIO_VERYLOW) - assert p.ionice() == psutil.IOPRIO_VERYLOW - p.ionice(psutil.IOPRIO_LOW) - assert p.ionice() == psutil.IOPRIO_LOW - try: - p.ionice(psutil.IOPRIO_HIGH) - except psutil.AccessDenied: - pass - else: - assert p.ionice() == psutil.IOPRIO_HIGH - # errs - with pytest.raises( - TypeError, match="value argument not accepted on Windows" - ): - p.ionice(psutil.IOPRIO_NORMAL, value=1) - with pytest.raises(ValueError, match="is not a valid priority"): - p.ionice(psutil.IOPRIO_HIGH + 1) - - @pytest.mark.skipif(not HAS_RLIMIT, reason="not supported") - def test_rlimit_get(self): - import resource - - p = psutil.Process(os.getpid()) - names = [x for x in dir(psutil) if x.startswith('RLIMIT')] - assert names, names - for name in names: - value = getattr(psutil, name) - assert value >= 0 - if name in dir(resource): - assert value == getattr(resource, name) - # XXX - On PyPy RLIMIT_INFINITY returned by - # resource.getrlimit() is reported as a very big long - # number instead of -1. It looks like a bug with PyPy. - if PYPY: - continue - assert p.rlimit(value) == resource.getrlimit(value) - else: - ret = p.rlimit(value) - assert len(ret) == 2 - assert ret[0] >= -1 - assert ret[1] >= -1 - - @pytest.mark.skipif(not HAS_RLIMIT, reason="not supported") - def test_rlimit_set(self): - p = self.spawn_psproc() - p.rlimit(psutil.RLIMIT_NOFILE, (5, 5)) - assert p.rlimit(psutil.RLIMIT_NOFILE) == (5, 5) - # If pid is 0 prlimit() applies to the calling process and - # we don't want that. - if LINUX: - with pytest.raises(ValueError, match="can't use prlimit"): - psutil._psplatform.Process(0).rlimit(0) - with pytest.raises(ValueError): - p.rlimit(psutil.RLIMIT_NOFILE, (5, 5, 5)) - - @pytest.mark.skipif(not HAS_RLIMIT, reason="not supported") - def test_rlimit(self): - p = psutil.Process() - testfn = self.get_testfn() - soft, hard = p.rlimit(psutil.RLIMIT_FSIZE) - try: - p.rlimit(psutil.RLIMIT_FSIZE, (1024, hard)) - with open(testfn, "wb") as f: - f.write(b"X" * 1024) - # write() or flush() doesn't always cause the exception - # but close() will. - with pytest.raises(OSError) as exc: - with open(testfn, "wb") as f: - f.write(b"X" * 1025) - assert exc.value.errno == errno.EFBIG - finally: - p.rlimit(psutil.RLIMIT_FSIZE, (soft, hard)) - assert p.rlimit(psutil.RLIMIT_FSIZE) == (soft, hard) - - @pytest.mark.skipif(not HAS_RLIMIT, reason="not supported") - def test_rlimit_infinity(self): - # First set a limit, then re-set it by specifying INFINITY - # and assume we overridden the previous limit. - p = psutil.Process() - soft, hard = p.rlimit(psutil.RLIMIT_FSIZE) - try: - p.rlimit(psutil.RLIMIT_FSIZE, (1024, hard)) - p.rlimit(psutil.RLIMIT_FSIZE, (psutil.RLIM_INFINITY, hard)) - with open(self.get_testfn(), "wb") as f: - f.write(b"X" * 2048) - finally: - p.rlimit(psutil.RLIMIT_FSIZE, (soft, hard)) - assert p.rlimit(psutil.RLIMIT_FSIZE) == (soft, hard) - - @pytest.mark.skipif(not HAS_RLIMIT, reason="not supported") - def test_rlimit_infinity_value(self): - # RLIMIT_FSIZE should be RLIM_INFINITY, which will be a really - # big number on a platform with large file support. On these - # platforms we need to test that the get/setrlimit functions - # properly convert the number to a C long long and that the - # conversion doesn't raise an error. - p = psutil.Process() - soft, hard = p.rlimit(psutil.RLIMIT_FSIZE) - assert hard == psutil.RLIM_INFINITY - p.rlimit(psutil.RLIMIT_FSIZE, (soft, hard)) - - def test_num_threads(self): - # on certain platforms such as Linux we might test for exact - # thread number, since we always have with 1 thread per process, - # but this does not apply across all platforms (MACOS, Windows) - p = psutil.Process() - if OPENBSD: - try: - step1 = p.num_threads() - except psutil.AccessDenied: - raise pytest.skip("on OpenBSD this requires root access") - else: - step1 = p.num_threads() - - with ThreadTask(): - step2 = p.num_threads() - assert step2 == step1 + 1 - - @pytest.mark.skipif(not WINDOWS, reason="WINDOWS only") - def test_num_handles(self): - # a better test is done later into test/_windows.py - p = psutil.Process() - assert p.num_handles() > 0 - - @pytest.mark.skipif(not HAS_THREADS, reason="not supported") - def test_threads(self): - p = psutil.Process() - if OPENBSD: - try: - step1 = p.threads() - except psutil.AccessDenied: - raise pytest.skip("on OpenBSD this requires root access") - else: - step1 = p.threads() - - with ThreadTask(): - step2 = p.threads() - assert len(step2) == len(step1) + 1 - athread = step2[0] - # test named tuple - assert athread.id == athread[0] - assert athread.user_time == athread[1] - assert athread.system_time == athread[2] - - @retry_on_failure() - @skip_on_access_denied(only_if=MACOS) - @pytest.mark.skipif(not HAS_THREADS, reason="not supported") - def test_threads_2(self): - p = self.spawn_psproc() - if OPENBSD: - try: - p.threads() - except psutil.AccessDenied: - raise pytest.skip("on OpenBSD this requires root access") - assert ( - abs(p.cpu_times().user - sum(x.user_time for x in p.threads())) - < 0.1 - ) - assert ( - abs(p.cpu_times().system - sum(x.system_time for x in p.threads())) - < 0.1 - ) - - @retry_on_failure() - def test_memory_info(self): - p = psutil.Process() - - # step 1 - get a base value to compare our results - rss1, vms1 = p.memory_info()[:2] - percent1 = p.memory_percent() - assert rss1 > 0 - assert vms1 > 0 - - # step 2 - allocate some memory - memarr = [None] * 1500000 - - rss2, vms2 = p.memory_info()[:2] - percent2 = p.memory_percent() - - # step 3 - make sure that the memory usage bumped up - assert rss2 > rss1 - assert vms2 >= vms1 # vms might be equal - assert percent2 > percent1 - del memarr - - if WINDOWS: - mem = p.memory_info() - assert mem.rss == mem.wset - assert mem.vms == mem.pagefile - - mem = p.memory_info() - for name in mem._fields: - assert getattr(mem, name) >= 0 - - def test_memory_full_info(self): - p = psutil.Process() - total = psutil.virtual_memory().total - mem = p.memory_full_info() - for name in mem._fields: - value = getattr(mem, name) - assert value >= 0 - if (name == "vms" and OSX) or LINUX: - continue - assert value <= total - if LINUX or WINDOWS or MACOS: - assert mem.uss >= 0 - if LINUX: - assert mem.pss >= 0 - assert mem.swap >= 0 - - @pytest.mark.skipif(not HAS_MEMORY_MAPS, reason="not supported") - def test_memory_maps(self): - p = psutil.Process() - maps = p.memory_maps() - assert len(maps) == len(set(maps)) - ext_maps = p.memory_maps(grouped=False) - - for nt in maps: - if nt.path.startswith('['): - continue - if BSD and nt.path == "pvclock": - continue - assert os.path.isabs(nt.path), nt.path - - if POSIX: - try: - assert os.path.exists(nt.path) or os.path.islink( - nt.path - ), nt.path - except AssertionError: - if not LINUX: - raise - # https://github.com/giampaolo/psutil/issues/759 - with open_text('/proc/self/smaps') as f: - data = f.read() - if f"{nt.path} (deleted)" not in data: - raise - elif '64' not in os.path.basename(nt.path): - # XXX - On Windows we have this strange behavior with - # 64 bit dlls: they are visible via explorer but cannot - # be accessed via os.stat() (wtf?). - try: - st = os.stat(nt.path) - except FileNotFoundError: - pass - else: - assert stat.S_ISREG(st.st_mode), nt.path - - for nt in ext_maps: - for fname in nt._fields: - value = getattr(nt, fname) - if fname == 'path': - continue - if fname in {'addr', 'perms'}: - assert value, value - else: - assert isinstance(value, int) - assert value >= 0, value - - @pytest.mark.skipif(not HAS_MEMORY_MAPS, reason="not supported") - def test_memory_maps_lists_lib(self): - # Make sure a newly loaded shared lib is listed. - p = psutil.Process() - with copyload_shared_lib() as path: - - def normpath(p): - return os.path.realpath(os.path.normcase(p)) - - libpaths = [normpath(x.path) for x in p.memory_maps()] - assert normpath(path) in libpaths - - def test_memory_percent(self): - p = psutil.Process() - p.memory_percent() - with pytest.raises(ValueError): - p.memory_percent(memtype="?!?") - if LINUX or MACOS or WINDOWS: - p.memory_percent(memtype='uss') - - def test_is_running(self): - p = self.spawn_psproc() - assert p.is_running() - assert p.is_running() - p.kill() - p.wait() - assert not p.is_running() - assert not p.is_running() - - def test_exe(self): - p = self.spawn_psproc() - exe = p.exe() - try: - assert exe == PYTHON_EXE - except AssertionError: - if WINDOWS and len(exe) == len(PYTHON_EXE): - # on Windows we don't care about case sensitivity - normcase = os.path.normcase - assert normcase(exe) == normcase(PYTHON_EXE) - else: - # certain platforms such as BSD are more accurate returning: - # "/usr/local/bin/python3.7" - # ...instead of: - # "/usr/local/bin/python" - # We do not want to consider this difference in accuracy - # an error. - ver = f"{sys.version_info[0]}.{sys.version_info[1]}" - try: - assert exe.replace(ver, '') == PYTHON_EXE.replace(ver, '') - except AssertionError: - # Typically MACOS. Really not sure what to do here. - pass - - out = sh([exe, "-c", "import os; print('hey')"]) - assert out == 'hey' - - def test_cmdline(self): - cmdline = [ - PYTHON_EXE, - "-c", - "import time; [time.sleep(0.1) for x in range(100)]", - ] - p = self.spawn_psproc(cmdline) - - if NETBSD and p.cmdline() == []: - # https://github.com/giampaolo/psutil/issues/2250 - raise pytest.skip("OPENBSD: returned EBUSY") - - # XXX - most of the times the underlying sysctl() call on Net - # and Open BSD returns a truncated string. - # Also /proc/pid/cmdline behaves the same so it looks - # like this is a kernel bug. - # XXX - AIX truncates long arguments in /proc/pid/cmdline - if NETBSD or OPENBSD or AIX: - assert p.cmdline()[0] == PYTHON_EXE - else: - if MACOS and CI_TESTING: - pyexe = p.cmdline()[0] - if pyexe != PYTHON_EXE: - assert ' '.join(p.cmdline()[1:]) == ' '.join(cmdline[1:]) - return - assert ' '.join(p.cmdline()) == ' '.join(cmdline) - - @pytest.mark.skipif(PYPY, reason="broken on PYPY") - def test_long_cmdline(self): - cmdline = [PYTHON_EXE] - cmdline.extend(["-v"] * 50) - cmdline.extend( - ["-c", "import time; [time.sleep(0.1) for x in range(100)]"] - ) - p = self.spawn_psproc(cmdline) - if OPENBSD: - # XXX: for some reason the test process may turn into a - # zombie (don't know why). - try: - assert p.cmdline() == cmdline - except psutil.ZombieProcess: - raise pytest.skip("OPENBSD: process turned into zombie") - else: - ret = p.cmdline() - if NETBSD and ret == []: - # https://github.com/giampaolo/psutil/issues/2250 - raise pytest.skip("OPENBSD: returned EBUSY") - assert ret == cmdline - - def test_name(self): - p = self.spawn_psproc() - name = p.name().lower() - pyexe = os.path.basename(os.path.realpath(sys.executable)).lower() - assert pyexe.startswith(name), (pyexe, name) - - @pytest.mark.skipif(PYPY, reason="unreliable on PYPY") - def test_long_name(self): - pyexe = create_py_exe(self.get_testfn(suffix=string.digits * 2)) - cmdline = [ - pyexe, - "-c", - "import time; [time.sleep(0.1) for x in range(100)]", - ] - p = self.spawn_psproc(cmdline) - if OPENBSD: - # XXX: for some reason the test process may turn into a - # zombie (don't know why). Because the name() is long, all - # UNIX kernels truncate it to 15 chars, so internally psutil - # tries to guess the full name() from the cmdline(). But the - # cmdline() of a zombie on OpenBSD fails (internally), so we - # just compare the first 15 chars. Full explanation: - # https://github.com/giampaolo/psutil/issues/2239 - try: - assert p.name() == os.path.basename(pyexe) - except AssertionError: - if p.status() == psutil.STATUS_ZOMBIE: - assert os.path.basename(pyexe).startswith(p.name()) - else: - raise - else: - assert p.name() == os.path.basename(pyexe) - - # XXX: fails too often - # @pytest.mark.skipif(SUNOS, reason="broken on SUNOS") - # @pytest.mark.skipif(AIX, reason="broken on AIX") - # @pytest.mark.skipif(PYPY, reason="broken on PYPY") - # def test_prog_w_funky_name(self): - # # Test that name(), exe() and cmdline() correctly handle programs - # # with funky chars such as spaces and ")", see: - # # https://github.com/giampaolo/psutil/issues/628 - # pyexe = create_py_exe(self.get_testfn(suffix='foo bar )')) - # cmdline = [ - # pyexe, - # "-c", - # "import time; [time.sleep(0.1) for x in range(100)]", - # ] - # p = self.spawn_psproc(cmdline) - # assert p.cmdline() == cmdline - # assert p.name() == os.path.basename(pyexe) - # assert os.path.normcase(p.exe()) == os.path.normcase(pyexe) - - @pytest.mark.skipif(not POSIX, reason="POSIX only") - def test_uids(self): - p = psutil.Process() - real, effective, _saved = p.uids() - # os.getuid() refers to "real" uid - assert real == os.getuid() - # os.geteuid() refers to "effective" uid - assert effective == os.geteuid() - # No such thing as os.getsuid() ("saved" uid), but we have - # os.getresuid() which returns all of them. - if hasattr(os, "getresuid"): - assert os.getresuid() == p.uids() - - @pytest.mark.skipif(not POSIX, reason="POSIX only") - def test_gids(self): - p = psutil.Process() - real, effective, _saved = p.gids() - # os.getuid() refers to "real" uid - assert real == os.getgid() - # os.geteuid() refers to "effective" uid - assert effective == os.getegid() - # No such thing as os.getsgid() ("saved" gid), but we have - # os.getresgid() which returns all of them. - if hasattr(os, "getresuid"): - assert os.getresgid() == p.gids() - - def test_nice(self): - def cleanup(init): - try: - p.nice(init) - except psutil.AccessDenied: - pass - - p = psutil.Process() - with pytest.raises(TypeError): - p.nice("str") - init = p.nice() - self.addCleanup(cleanup, init) - - if WINDOWS: - highest_prio = None - for prio in [ - psutil.IDLE_PRIORITY_CLASS, - psutil.BELOW_NORMAL_PRIORITY_CLASS, - psutil.NORMAL_PRIORITY_CLASS, - psutil.ABOVE_NORMAL_PRIORITY_CLASS, - psutil.HIGH_PRIORITY_CLASS, - psutil.REALTIME_PRIORITY_CLASS, - ]: - with self.subTest(prio=prio): - try: - p.nice(prio) - except psutil.AccessDenied: - pass - else: - new_prio = p.nice() - # The OS may limit our maximum priority, - # even if the function succeeds. For higher - # priorities, we match either the expected - # value or the highest so far. - if prio in { - psutil.ABOVE_NORMAL_PRIORITY_CLASS, - psutil.HIGH_PRIORITY_CLASS, - psutil.REALTIME_PRIORITY_CLASS, - }: - if new_prio == prio or highest_prio is None: - highest_prio = prio - assert new_prio == highest_prio - else: - assert new_prio == prio - else: - try: - if hasattr(os, "getpriority"): - assert ( - os.getpriority(os.PRIO_PROCESS, os.getpid()) - == p.nice() - ) - p.nice(1) - assert p.nice() == 1 - if hasattr(os, "getpriority"): - assert ( - os.getpriority(os.PRIO_PROCESS, os.getpid()) - == p.nice() - ) - # XXX - going back to previous nice value raises - # AccessDenied on MACOS - if not MACOS: - p.nice(0) - assert p.nice() == 0 - except psutil.AccessDenied: - pass - - def test_status(self): - p = psutil.Process() - assert p.status() == psutil.STATUS_RUNNING - - def test_username(self): - p = self.spawn_psproc() - username = p.username() - if WINDOWS: - domain, username = username.split('\\') - getpass_user = getpass.getuser() - if getpass_user.endswith('$'): - # When running as a service account (most likely to be - # NetworkService), these user name calculations don't produce - # the same result, causing the test to fail. - raise pytest.skip('running as service account') - assert username == getpass_user - if 'USERDOMAIN' in os.environ: - assert domain == os.environ['USERDOMAIN'] - else: - assert username == getpass.getuser() - - def test_cwd(self): - p = self.spawn_psproc() - assert p.cwd() == os.getcwd() - - def test_cwd_2(self): - cmd = [ - PYTHON_EXE, - "-c", - ( - "import os, time; os.chdir('..'); [time.sleep(0.1) for x in" - " range(100)]" - ), - ] - p = self.spawn_psproc(cmd) - call_until(lambda: p.cwd() == os.path.dirname(os.getcwd())) - - @pytest.mark.skipif(not HAS_CPU_AFFINITY, reason="not supported") - def test_cpu_affinity(self): - p = psutil.Process() - initial = p.cpu_affinity() - assert initial, initial - self.addCleanup(p.cpu_affinity, initial) - - if hasattr(os, "sched_getaffinity"): - assert initial == list(os.sched_getaffinity(p.pid)) - assert len(initial) == len(set(initial)) - - all_cpus = list(range(len(psutil.cpu_percent(percpu=True)))) - for n in all_cpus: - p.cpu_affinity([n]) - assert p.cpu_affinity() == [n] - if hasattr(os, "sched_getaffinity"): - assert p.cpu_affinity() == list(os.sched_getaffinity(p.pid)) - # also test num_cpu() - if hasattr(p, "num_cpu"): - assert p.cpu_affinity()[0] == p.num_cpu() - - # [] is an alias for "all eligible CPUs"; on Linux this may - # not be equal to all available CPUs, see: - # https://github.com/giampaolo/psutil/issues/956 - p.cpu_affinity([]) - if LINUX: - assert p.cpu_affinity() == p._proc._get_eligible_cpus() - else: - assert p.cpu_affinity() == all_cpus - if hasattr(os, "sched_getaffinity"): - assert p.cpu_affinity() == list(os.sched_getaffinity(p.pid)) - - with pytest.raises(TypeError): - p.cpu_affinity(1) - p.cpu_affinity(initial) - # it should work with all iterables, not only lists - p.cpu_affinity(set(all_cpus)) - p.cpu_affinity(tuple(all_cpus)) - - @pytest.mark.skipif(not HAS_CPU_AFFINITY, reason="not supported") - def test_cpu_affinity_errs(self): - p = self.spawn_psproc() - invalid_cpu = [len(psutil.cpu_times(percpu=True)) + 10] - with pytest.raises(ValueError): - p.cpu_affinity(invalid_cpu) - with pytest.raises(ValueError): - p.cpu_affinity(range(10000, 11000)) - with pytest.raises((TypeError, ValueError)): - p.cpu_affinity([0, "1"]) - with pytest.raises(ValueError): - p.cpu_affinity([0, -1]) - - @pytest.mark.skipif(not HAS_CPU_AFFINITY, reason="not supported") - def test_cpu_affinity_all_combinations(self): - p = psutil.Process() - initial = p.cpu_affinity() - assert initial, initial - self.addCleanup(p.cpu_affinity, initial) - - # All possible CPU set combinations. - if len(initial) > 12: - initial = initial[:12] # ...otherwise it will take forever - combos = [] - for i in range(len(initial) + 1): - combos.extend( - list(subset) - for subset in itertools.combinations(initial, i) - if subset - ) - - for combo in combos: - p.cpu_affinity(combo) - assert sorted(p.cpu_affinity()) == sorted(combo) - - # TODO: #595 - @pytest.mark.skipif(BSD, reason="broken on BSD") - def test_open_files(self): - p = psutil.Process() - testfn = self.get_testfn() - files = p.open_files() - assert testfn not in files - with open(testfn, 'wb') as f: - f.write(b'x' * 1024) - f.flush() - # give the kernel some time to see the new file - call_until(lambda: len(p.open_files()) != len(files)) - files = p.open_files() - filenames = [os.path.normcase(x.path) for x in files] - assert os.path.normcase(testfn) in filenames - if LINUX: - for file in files: - if file.path == testfn: - assert file.position == 1024 - for file in files: - assert os.path.isfile(file.path), file - - # another process - cmdline = ( - f"import time; f = open(r'{testfn}', 'r'); [time.sleep(0.1) for x" - " in range(100)];" - ) - p = self.spawn_psproc([PYTHON_EXE, "-c", cmdline]) - - for x in range(100): - filenames = [os.path.normcase(x.path) for x in p.open_files()] - if testfn in filenames: - break - time.sleep(0.01) - else: - assert os.path.normcase(testfn) in filenames - for file in filenames: - assert os.path.isfile(file), file - - # TODO: #595 - @pytest.mark.skipif(BSD, reason="broken on BSD") - def test_open_files_2(self): - # test fd and path fields - p = psutil.Process() - normcase = os.path.normcase - testfn = self.get_testfn() - with open(testfn, 'w') as fileobj: - for file in p.open_files(): - if ( - normcase(file.path) == normcase(fileobj.name) - or file.fd == fileobj.fileno() - ): - break - else: - raise self.fail(f"no file found; files={p.open_files()!r}") - assert normcase(file.path) == normcase(fileobj.name) - if WINDOWS: - assert file.fd == -1 - else: - assert file.fd == fileobj.fileno() - # test positions - ntuple = p.open_files()[0] - assert ntuple[0] == ntuple.path - assert ntuple[1] == ntuple.fd - # test file is gone - assert fileobj.name not in p.open_files() - - @pytest.mark.skipif(not POSIX, reason="POSIX only") - def test_num_fds(self): - p = psutil.Process() - testfn = self.get_testfn() - start = p.num_fds() - file = open(testfn, 'w') # noqa: SIM115 - self.addCleanup(file.close) - assert p.num_fds() == start + 1 - sock = socket.socket() - self.addCleanup(sock.close) - assert p.num_fds() == start + 2 - file.close() - sock.close() - assert p.num_fds() == start - - @skip_on_not_implemented(only_if=LINUX) - @pytest.mark.skipif( - OPENBSD or NETBSD, reason="not reliable on OPENBSD & NETBSD" - ) - def test_num_ctx_switches(self): - p = psutil.Process() - before = sum(p.num_ctx_switches()) - for _ in range(2): - time.sleep(0.05) # this shall ensure a context switch happens - after = sum(p.num_ctx_switches()) - if after > before: - return - raise self.fail("num ctx switches still the same after 2 iterations") - - def test_ppid(self): - p = psutil.Process() - if hasattr(os, 'getppid'): - assert p.ppid() == os.getppid() - p = self.spawn_psproc() - assert p.ppid() == os.getpid() - - def test_parent(self): - p = self.spawn_psproc() - assert p.parent().pid == os.getpid() - - lowest_pid = psutil.pids()[0] - assert psutil.Process(lowest_pid).parent() is None - - def test_parent_multi(self): - parent = psutil.Process() - child, grandchild = self.spawn_children_pair() - assert grandchild.parent() == child - assert child.parent() == parent - - @retry_on_failure() - def test_parents(self): - parent = psutil.Process() - assert parent.parents() - child, grandchild = self.spawn_children_pair() - assert child.parents()[0] == parent - assert grandchild.parents()[0] == child - assert grandchild.parents()[1] == parent - - def test_children(self): - parent = psutil.Process() - assert not parent.children() - assert not parent.children(recursive=True) - # On Windows we set the flag to 0 in order to cancel out the - # CREATE_NO_WINDOW flag (enabled by default) which creates - # an extra "conhost.exe" child. - child = self.spawn_psproc(creationflags=0) - children1 = parent.children() - children2 = parent.children(recursive=True) - for children in (children1, children2): - assert len(children) == 1 - assert children[0].pid == child.pid - assert children[0].ppid() == parent.pid - - def test_children_recursive(self): - # Test children() against two sub processes, p1 and p2, where - # p1 (our child) spawned p2 (our grandchild). - parent = psutil.Process() - child, grandchild = self.spawn_children_pair() - assert parent.children() == [child] - assert parent.children(recursive=True) == [child, grandchild] - # If the intermediate process is gone there's no way for - # children() to recursively find it. - child.terminate() - child.wait() - assert not parent.children(recursive=True) - - def test_children_duplicates(self): - # find the process which has the highest number of children - table = collections.defaultdict(int) - for p in psutil.process_iter(): - try: - table[p.ppid()] += 1 - except psutil.Error: - pass - # this is the one, now let's make sure there are no duplicates - pid = max(table.items(), key=lambda x: x[1])[0] - if LINUX and pid == 0: - raise pytest.skip("PID 0") - p = psutil.Process(pid) - try: - c = p.children(recursive=True) - except psutil.AccessDenied: # windows - pass - else: - assert len(c) == len(set(c)) - - def test_parents_and_children(self): - parent = psutil.Process() - child, grandchild = self.spawn_children_pair() - # forward - children = parent.children(recursive=True) - assert len(children) == 2 - assert children[0] == child - assert children[1] == grandchild - # backward - parents = grandchild.parents() - assert parents[0] == child - assert parents[1] == parent - - def test_suspend_resume(self): - p = self.spawn_psproc() - p.suspend() - for _ in range(100): - if p.status() == psutil.STATUS_STOPPED: - break - time.sleep(0.01) - p.resume() - assert p.status() != psutil.STATUS_STOPPED - - def test_invalid_pid(self): - with pytest.raises(TypeError): - psutil.Process("1") - with pytest.raises(ValueError): - psutil.Process(-1) - - def test_as_dict(self): - p = psutil.Process() - d = p.as_dict(attrs=['exe', 'name']) - assert sorted(d.keys()) == ['exe', 'name'] - - p = psutil.Process(min(psutil.pids())) - d = p.as_dict(attrs=['net_connections'], ad_value='foo') - if not isinstance(d['net_connections'], list): - assert d['net_connections'] == 'foo' - - # Test ad_value is set on AccessDenied. - with mock.patch( - 'psutil.Process.nice', create=True, side_effect=psutil.AccessDenied - ): - assert p.as_dict(attrs=["nice"], ad_value=1) == {"nice": 1} - - # Test that NoSuchProcess bubbles up. - with mock.patch( - 'psutil.Process.nice', - create=True, - side_effect=psutil.NoSuchProcess(p.pid, "name"), - ): - with pytest.raises(psutil.NoSuchProcess): - p.as_dict(attrs=["nice"]) - - # Test that ZombieProcess is swallowed. - with mock.patch( - 'psutil.Process.nice', - create=True, - side_effect=psutil.ZombieProcess(p.pid, "name"), - ): - assert p.as_dict(attrs=["nice"], ad_value="foo") == {"nice": "foo"} - - # By default APIs raising NotImplementedError are - # supposed to be skipped. - with mock.patch( - 'psutil.Process.nice', create=True, side_effect=NotImplementedError - ): - d = p.as_dict() - assert 'nice' not in list(d.keys()) - # ...unless the user explicitly asked for some attr. - with pytest.raises(NotImplementedError): - p.as_dict(attrs=["nice"]) - - # errors - with pytest.raises(TypeError): - p.as_dict('name') - with pytest.raises(ValueError): - p.as_dict(['foo']) - with pytest.raises(ValueError): - p.as_dict(['foo', 'bar']) - - def test_oneshot(self): - p = psutil.Process() - with mock.patch("psutil._psplatform.Process.cpu_times") as m: - with p.oneshot(): - p.cpu_times() - p.cpu_times() - assert m.call_count == 1 - - with mock.patch("psutil._psplatform.Process.cpu_times") as m: - p.cpu_times() - p.cpu_times() - assert m.call_count == 2 - - def test_oneshot_twice(self): - # Test the case where the ctx manager is __enter__ed twice. - # The second __enter__ is supposed to resut in a NOOP. - p = psutil.Process() - with mock.patch("psutil._psplatform.Process.cpu_times") as m1: - with mock.patch("psutil._psplatform.Process.oneshot_enter") as m2: - with p.oneshot(): - p.cpu_times() - p.cpu_times() - with p.oneshot(): - p.cpu_times() - p.cpu_times() - assert m1.call_count == 1 - assert m2.call_count == 1 - - with mock.patch("psutil._psplatform.Process.cpu_times") as m: - p.cpu_times() - p.cpu_times() - assert m.call_count == 2 - - def test_oneshot_cache(self): - # Make sure oneshot() cache is nonglobal. Instead it's - # supposed to be bound to the Process instance, see: - # https://github.com/giampaolo/psutil/issues/1373 - p1, p2 = self.spawn_children_pair() - p1_ppid = p1.ppid() - p2_ppid = p2.ppid() - assert p1_ppid != p2_ppid - with p1.oneshot(): - assert p1.ppid() == p1_ppid - assert p2.ppid() == p2_ppid - with p2.oneshot(): - assert p1.ppid() == p1_ppid - assert p2.ppid() == p2_ppid - - def test_halfway_terminated_process(self): - # Test that NoSuchProcess exception gets raised in case the - # process dies after we create the Process object. - # Example: - # >>> proc = Process(1234) - # >>> time.sleep(2) # time-consuming task, process dies in meantime - # >>> proc.name() - # Refers to Issue #15 - def assert_raises_nsp(fun, fun_name): - try: - ret = fun() - except psutil.ZombieProcess: # differentiate from NSP - raise - except psutil.NoSuchProcess: - pass - except psutil.AccessDenied: - if OPENBSD and fun_name in {'threads', 'num_threads'}: - return - raise - else: - # NtQuerySystemInformation succeeds even if process is gone. - if WINDOWS and fun_name in {'exe', 'name'}: - return - raise self.fail( - f"{fun!r} didn't raise NSP and returned {ret!r} instead" - ) - - p = self.spawn_psproc() - p.terminate() - p.wait() - if WINDOWS: # XXX - call_until(lambda: p.pid not in psutil.pids()) - self.assertProcessGone(p) - - ns = process_namespace(p) - for fun, name in ns.iter(ns.all): - assert_raises_nsp(fun, name) - - @pytest.mark.skipif(not POSIX, reason="POSIX only") - def test_zombie_process(self): - _parent, zombie = self.spawn_zombie() - self.assertProcessZombie(zombie) - - @pytest.mark.skipif(not POSIX, reason="POSIX only") - def test_zombie_process_is_running_w_exc(self): - # Emulate a case where internally is_running() raises - # ZombieProcess. - p = psutil.Process() - with mock.patch( - "psutil.Process", side_effect=psutil.ZombieProcess(0) - ) as m: - assert p.is_running() - assert m.called - - @pytest.mark.skipif(not POSIX, reason="POSIX only") - def test_zombie_process_status_w_exc(self): - # Emulate a case where internally status() raises - # ZombieProcess. - p = psutil.Process() - with mock.patch( - "psutil._psplatform.Process.status", - side_effect=psutil.ZombieProcess(0), - ) as m: - assert p.status() == psutil.STATUS_ZOMBIE - assert m.called - - def test_reused_pid(self): - # Emulate a case where PID has been reused by another process. - subp = self.spawn_testproc() - p = psutil.Process(subp.pid) - p._ident = (p.pid, p.create_time() + 100) - - list(psutil.process_iter()) - assert p.pid in psutil._pmap - assert not p.is_running() - - # make sure is_running() removed PID from process_iter() - # internal cache - with mock.patch.object(psutil._common, "PSUTIL_DEBUG", True): - with contextlib.redirect_stderr(io.StringIO()) as f: - list(psutil.process_iter()) - assert ( - f"refreshing Process instance for reused PID {p.pid}" - in f.getvalue() - ) - assert p.pid not in psutil._pmap - - assert p != psutil.Process(subp.pid) - msg = "process no longer exists and its PID has been reused" - ns = process_namespace(p) - for fun, name in ns.iter(ns.setters + ns.killers, clear_cache=False): - with self.subTest(name=name): - with pytest.raises(psutil.NoSuchProcess, match=msg): - fun() - - assert "terminated + PID reused" in str(p) - assert "terminated + PID reused" in repr(p) - - with pytest.raises(psutil.NoSuchProcess, match=msg): - p.ppid() - with pytest.raises(psutil.NoSuchProcess, match=msg): - p.parent() - with pytest.raises(psutil.NoSuchProcess, match=msg): - p.parents() - with pytest.raises(psutil.NoSuchProcess, match=msg): - p.children() - - def test_pid_0(self): - # Process(0) is supposed to work on all platforms except Linux - if 0 not in psutil.pids(): - with pytest.raises(psutil.NoSuchProcess): - psutil.Process(0) - # These 2 are a contradiction, but "ps" says PID 1's parent - # is PID 0. - assert not psutil.pid_exists(0) - assert psutil.Process(1).ppid() == 0 - return - - p = psutil.Process(0) - exc = psutil.AccessDenied if WINDOWS else ValueError - with pytest.raises(exc): - p.wait() - with pytest.raises(exc): - p.terminate() - with pytest.raises(exc): - p.suspend() - with pytest.raises(exc): - p.resume() - with pytest.raises(exc): - p.kill() - with pytest.raises(exc): - p.send_signal(signal.SIGTERM) - - # test all methods - ns = process_namespace(p) - for fun, name in ns.iter(ns.getters + ns.setters): - try: - ret = fun() - except psutil.AccessDenied: - pass - else: - if name in {"uids", "gids"}: - assert ret.real == 0 - elif name == "username": - user = 'NT AUTHORITY\\SYSTEM' if WINDOWS else 'root' - assert p.username() == user - elif name == "name": - assert name, name - - if not OPENBSD: - assert 0 in psutil.pids() - assert psutil.pid_exists(0) - - @pytest.mark.skipif(not HAS_ENVIRON, reason="not supported") - def test_environ(self): - def clean_dict(d): - exclude = ["PLAT", "HOME", "PYTEST_CURRENT_TEST", "PYTEST_VERSION"] - if MACOS: - exclude.extend([ - "__CF_USER_TEXT_ENCODING", - "VERSIONER_PYTHON_PREFER_32_BIT", - "VERSIONER_PYTHON_VERSION", - "VERSIONER_PYTHON_VERSION", - ]) - for name in exclude: - d.pop(name, None) - return { - k.replace("\r", "").replace("\n", ""): v.replace( - "\r", "" - ).replace("\n", "") - for k, v in d.items() - } - - self.maxDiff = None - p = psutil.Process() - d1 = clean_dict(p.environ()) - d2 = clean_dict(os.environ.copy()) - if not OSX and GITHUB_ACTIONS: - assert d1 == d2 - - @pytest.mark.skipif(not HAS_ENVIRON, reason="not supported") - @pytest.mark.skipif(not POSIX, reason="POSIX only") - @pytest.mark.skipif( - MACOS_11PLUS, - reason="macOS 11+ can't get another process environment, issue #2084", - ) - @pytest.mark.skipif( - NETBSD, reason="sometimes fails on `assert is_running()`" - ) - def test_weird_environ(self): - # environment variables can contain values without an equals sign - code = textwrap.dedent(""" - #include - #include - - char * const argv[] = {"cat", 0}; - char * const envp[] = {"A=1", "X", "C=3", 0}; - - int main(void) { - // Close stderr on exec so parent can wait for the - // execve to finish. - if (fcntl(2, F_SETFD, FD_CLOEXEC) != 0) - return 0; - return execve("/bin/cat", argv, envp); - } - """) - cexe = create_c_exe(self.get_testfn(), c_code=code) - sproc = self.spawn_testproc( - [cexe], stdin=subprocess.PIPE, stderr=subprocess.PIPE - ) - p = psutil.Process(sproc.pid) - wait_for_pid(p.pid) - assert p.is_running() - # Wait for process to exec or exit. - assert sproc.stderr.read() == b"" - if MACOS and CI_TESTING: - try: - env = p.environ() - except psutil.AccessDenied: - # XXX: fails sometimes with: - # PermissionError from 'sysctl(KERN_PROCARGS2) -> EIO' - return - else: - env = p.environ() - assert env == {"A": "1", "C": "3"} - sproc.communicate() - assert sproc.returncode == 0 - - -# =================================================================== -# --- psutil.Popen tests -# =================================================================== - - -class TestPopen(PsutilTestCase): - """Tests for psutil.Popen class.""" - - @classmethod - def tearDownClass(cls): - reap_children() - - def test_misc(self): - # XXX this test causes a ResourceWarning because - # psutil.__subproc instance doesn't get properly freed. - # Not sure what to do though. - cmd = [ - PYTHON_EXE, - "-c", - "import time; [time.sleep(0.1) for x in range(100)];", - ] - with psutil.Popen( - cmd, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - env=PYTHON_EXE_ENV, - ) as proc: - proc.name() - proc.cpu_times() - proc.stdin # noqa: B018 - assert dir(proc) - with pytest.raises(AttributeError): - proc.foo # noqa: B018 - proc.terminate() - if POSIX: - assert proc.wait(5) == -signal.SIGTERM - else: - assert proc.wait(5) == signal.SIGTERM - - def test_ctx_manager(self): - with psutil.Popen( - [PYTHON_EXE, "-V"], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - stdin=subprocess.PIPE, - env=PYTHON_EXE_ENV, - ) as proc: - proc.communicate() - assert proc.stdout.closed - assert proc.stderr.closed - assert proc.stdin.closed - assert proc.returncode == 0 - - def test_kill_terminate(self): - # subprocess.Popen()'s terminate(), kill() and send_signal() do - # not raise exception after the process is gone. psutil.Popen - # diverges from that. - cmd = [ - PYTHON_EXE, - "-c", - "import time; [time.sleep(0.1) for x in range(100)];", - ] - with psutil.Popen( - cmd, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - env=PYTHON_EXE_ENV, - ) as proc: - proc.terminate() - proc.wait() - with pytest.raises(psutil.NoSuchProcess): - proc.terminate() - with pytest.raises(psutil.NoSuchProcess): - proc.kill() - with pytest.raises(psutil.NoSuchProcess): - proc.send_signal(signal.SIGTERM) - if WINDOWS: - with pytest.raises(psutil.NoSuchProcess): - proc.send_signal(signal.CTRL_C_EVENT) - with pytest.raises(psutil.NoSuchProcess): - proc.send_signal(signal.CTRL_BREAK_EVENT) - - def test__getattribute__(self): - cmd = [ - PYTHON_EXE, - "-c", - "import time; [time.sleep(0.1) for x in range(100)];", - ] - with psutil.Popen( - cmd, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - env=PYTHON_EXE_ENV, - ) as proc: - proc.terminate() - proc.wait() - with pytest.raises(AttributeError): - proc.foo # noqa: B018 diff --git a/PortablePython/Lib/site-packages/psutil/tests/test_process_all.py b/PortablePython/Lib/site-packages/psutil/tests/test_process_all.py deleted file mode 100644 index aaa3fa0..0000000 --- a/PortablePython/Lib/site-packages/psutil/tests/test_process_all.py +++ /dev/null @@ -1,535 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -"""Iterate over all process PIDs and for each one of them invoke and -test all psutil.Process() methods. -""" - -import enum -import errno -import multiprocessing -import os -import stat -import time -import traceback - -import psutil -from psutil import AIX -from psutil import BSD -from psutil import FREEBSD -from psutil import LINUX -from psutil import MACOS -from psutil import NETBSD -from psutil import OPENBSD -from psutil import OSX -from psutil import POSIX -from psutil import WINDOWS -from psutil.tests import CI_TESTING -from psutil.tests import PYTEST_PARALLEL -from psutil.tests import VALID_PROC_STATUSES -from psutil.tests import PsutilTestCase -from psutil.tests import check_connection_ntuple -from psutil.tests import create_sockets -from psutil.tests import is_namedtuple -from psutil.tests import is_win_secure_system_proc -from psutil.tests import process_namespace -from psutil.tests import pytest - - -# Cuts the time in half, but (e.g.) on macOS the process pool stays -# alive after join() (multiprocessing bug?), messing up other tests. -USE_PROC_POOL = LINUX and not CI_TESTING and not PYTEST_PARALLEL - - -def proc_info(pid): - tcase = PsutilTestCase() - - def check_exception(exc, proc, name, ppid): - tcase.assertEqual(exc.pid, pid) - if exc.name is not None: - tcase.assertEqual(exc.name, name) - if isinstance(exc, psutil.ZombieProcess): - tcase.assertProcessZombie(proc) - if exc.ppid is not None: - tcase.assertGreaterEqual(exc.ppid, 0) - tcase.assertEqual(exc.ppid, ppid) - elif isinstance(exc, psutil.NoSuchProcess): - tcase.assertProcessGone(proc) - str(exc) - repr(exc) - - def do_wait(): - if pid != 0: - try: - proc.wait(0) - except psutil.Error as exc: - check_exception(exc, proc, name, ppid) - - try: - proc = psutil.Process(pid) - except psutil.NoSuchProcess: - tcase.assertPidGone(pid) - return {} - try: - d = proc.as_dict(['ppid', 'name']) - except psutil.NoSuchProcess: - tcase.assertProcessGone(proc) - else: - name, ppid = d['name'], d['ppid'] - info = {'pid': proc.pid} - ns = process_namespace(proc) - # We don't use oneshot() because in order not to fool - # check_exception() in case of NSP. - for fun, fun_name in ns.iter(ns.getters, clear_cache=False): - try: - info[fun_name] = fun() - except psutil.Error as exc: - check_exception(exc, proc, name, ppid) - continue - do_wait() - return info - - -class TestFetchAllProcesses(PsutilTestCase): - """Test which iterates over all running processes and performs - some sanity checks against Process API's returned values. - Uses a process pool to get info about all processes. - """ - - def setUp(self): - psutil._set_debug(False) - # Using a pool in a CI env may result in deadlock, see: - # https://github.com/giampaolo/psutil/issues/2104 - if USE_PROC_POOL: - self.pool = multiprocessing.Pool() - - def tearDown(self): - psutil._set_debug(True) - if USE_PROC_POOL: - self.pool.terminate() - self.pool.join() - - def iter_proc_info(self): - # Fixes "can't pickle : it's not the - # same object as test_process_all.proc_info". - from psutil.tests.test_process_all import proc_info - - if USE_PROC_POOL: - return self.pool.imap_unordered(proc_info, psutil.pids()) - else: - ls = [proc_info(pid) for pid in psutil.pids()] - return ls - - def test_all(self): - failures = [] - for info in self.iter_proc_info(): - for name, value in info.items(): - meth = getattr(self, name) - try: - meth(value, info) - except Exception: # noqa: BLE001 - s = '\n' + '=' * 70 + '\n' - s += ( - "FAIL: name=test_{}, pid={}, ret={}\ninfo={}\n".format( - name, - info['pid'], - repr(value), - info, - ) - ) - s += '-' * 70 - s += f"\n{traceback.format_exc()}" - s = "\n".join((" " * 4) + i for i in s.splitlines()) + "\n" - failures.append(s) - else: - if value not in (0, 0.0, [], None, '', {}): - assert value, value - if failures: - raise self.fail(''.join(failures)) - - def cmdline(self, ret, info): - assert isinstance(ret, list) - for part in ret: - assert isinstance(part, str) - - def exe(self, ret, info): - assert isinstance(ret, str) - assert ret.strip() == ret - if ret: - if WINDOWS and not ret.endswith('.exe'): - return # May be "Registry", "MemCompression", ... - assert os.path.isabs(ret), ret - # Note: os.stat() may return False even if the file is there - # hence we skip the test, see: - # http://stackoverflow.com/questions/3112546/os-path-exists-lies - if POSIX and os.path.isfile(ret): - if hasattr(os, 'access') and hasattr(os, "X_OK"): - # XXX: may fail on MACOS - try: - assert os.access(ret, os.X_OK) - except AssertionError: - if os.path.exists(ret) and not CI_TESTING: - raise - - def pid(self, ret, info): - assert isinstance(ret, int) - assert ret >= 0 - - def ppid(self, ret, info): - assert isinstance(ret, int) - assert ret >= 0 - proc_info(ret) - - def name(self, ret, info): - assert isinstance(ret, str) - if WINDOWS and not ret and is_win_secure_system_proc(info['pid']): - # https://github.com/giampaolo/psutil/issues/2338 - return - # on AIX, "" processes don't have names - if not AIX: - assert ret, repr(ret) - - def create_time(self, ret, info): - assert isinstance(ret, float) - try: - assert ret >= 0 - except AssertionError: - # XXX - if OPENBSD and info['status'] == psutil.STATUS_ZOMBIE: - pass - else: - raise - # this can't be taken for granted on all platforms - # self.assertGreaterEqual(ret, psutil.boot_time()) - # make sure returned value can be pretty printed - # with strftime - time.strftime("%Y %m %d %H:%M:%S", time.localtime(ret)) - - def uids(self, ret, info): - assert is_namedtuple(ret) - for uid in ret: - assert isinstance(uid, int) - assert uid >= 0 - - def gids(self, ret, info): - assert is_namedtuple(ret) - # note: testing all gids as above seems not to be reliable for - # gid == 30 (nodoby); not sure why. - for gid in ret: - assert isinstance(gid, int) - if not MACOS and not NETBSD: - assert gid >= 0 - - def username(self, ret, info): - assert isinstance(ret, str) - assert ret.strip() == ret - assert ret.strip() - - def status(self, ret, info): - assert isinstance(ret, str) - assert ret, ret - assert ret != '?' # XXX - assert ret in VALID_PROC_STATUSES - - def io_counters(self, ret, info): - assert is_namedtuple(ret) - for field in ret: - assert isinstance(field, int) - if field != -1: - assert field >= 0 - - def ionice(self, ret, info): - if LINUX: - assert isinstance(ret.ioclass, int) - assert isinstance(ret.value, int) - assert ret.ioclass >= 0 - assert ret.value >= 0 - else: # Windows, Cygwin - choices = [ - psutil.IOPRIO_VERYLOW, - psutil.IOPRIO_LOW, - psutil.IOPRIO_NORMAL, - psutil.IOPRIO_HIGH, - ] - assert isinstance(ret, int) - assert ret >= 0 - assert ret in choices - - def num_threads(self, ret, info): - assert isinstance(ret, int) - if WINDOWS and ret == 0 and is_win_secure_system_proc(info['pid']): - # https://github.com/giampaolo/psutil/issues/2338 - return - assert ret >= 1 - - def threads(self, ret, info): - assert isinstance(ret, list) - for t in ret: - assert is_namedtuple(t) - assert t.id >= 0 - assert t.user_time >= 0 - assert t.system_time >= 0 - for field in t: - assert isinstance(field, (int, float)) - - def cpu_times(self, ret, info): - assert is_namedtuple(ret) - for n in ret: - assert isinstance(n, float) - assert n >= 0 - # TODO: check ntuple fields - - def cpu_percent(self, ret, info): - assert isinstance(ret, float) - assert 0.0 <= ret <= 100.0, ret - - def cpu_num(self, ret, info): - assert isinstance(ret, int) - if FREEBSD and ret == -1: - return - assert ret >= 0 - if psutil.cpu_count() == 1: - assert ret == 0 - assert ret in list(range(psutil.cpu_count())) - - def memory_info(self, ret, info): - assert is_namedtuple(ret) - for value in ret: - assert isinstance(value, int) - assert value >= 0 - if WINDOWS: - assert ret.peak_wset >= ret.wset - assert ret.peak_paged_pool >= ret.paged_pool - assert ret.peak_nonpaged_pool >= ret.nonpaged_pool - assert ret.peak_pagefile >= ret.pagefile - - def memory_full_info(self, ret, info): - assert is_namedtuple(ret) - total = psutil.virtual_memory().total - for name in ret._fields: - value = getattr(ret, name) - assert isinstance(value, int) - assert value >= 0 - if LINUX or (OSX and name in {'vms', 'data'}): - # On Linux there are processes (e.g. 'goa-daemon') whose - # VMS is incredibly high for some reason. - continue - assert value <= total, name - - if LINUX: - assert ret.pss >= ret.uss - - def open_files(self, ret, info): - assert isinstance(ret, list) - for f in ret: - assert isinstance(f.fd, int) - assert isinstance(f.path, str) - assert f.path.strip() == f.path - if WINDOWS: - assert f.fd == -1 - elif LINUX: - assert isinstance(f.position, int) - assert isinstance(f.mode, str) - assert isinstance(f.flags, int) - assert f.position >= 0 - assert f.mode in {'r', 'w', 'a', 'r+', 'a+'} - assert f.flags > 0 - elif BSD and not f.path: - # XXX see: https://github.com/giampaolo/psutil/issues/595 - continue - assert os.path.isabs(f.path), f - try: - st = os.stat(f.path) - except FileNotFoundError: - pass - else: - assert stat.S_ISREG(st.st_mode), f - - def num_fds(self, ret, info): - assert isinstance(ret, int) - assert ret >= 0 - - def net_connections(self, ret, info): - with create_sockets(): - assert len(ret) == len(set(ret)) - for conn in ret: - assert is_namedtuple(conn) - check_connection_ntuple(conn) - - def cwd(self, ret, info): - assert isinstance(ret, str) - assert ret.strip() == ret - if ret: - assert os.path.isabs(ret), ret - try: - st = os.stat(ret) - except OSError as err: - if WINDOWS and psutil._psplatform.is_permission_err(err): - pass - # directory has been removed in mean time - elif err.errno != errno.ENOENT: - raise - else: - assert stat.S_ISDIR(st.st_mode) - - def memory_percent(self, ret, info): - assert isinstance(ret, float) - assert 0 <= ret <= 100, ret - - def is_running(self, ret, info): - assert isinstance(ret, bool) - - def cpu_affinity(self, ret, info): - assert isinstance(ret, list) - assert ret != [] - cpus = list(range(psutil.cpu_count())) - for n in ret: - assert isinstance(n, int) - assert n in cpus - - def terminal(self, ret, info): - assert isinstance(ret, (str, type(None))) - if ret is not None: - assert os.path.isabs(ret), ret - assert os.path.exists(ret), ret - - def memory_maps(self, ret, info): - for nt in ret: - assert isinstance(nt.addr, str) - assert isinstance(nt.perms, str) - assert isinstance(nt.path, str) - for fname in nt._fields: - value = getattr(nt, fname) - if fname == 'path': - if value.startswith(("[", "anon_inode:")): # linux - continue - if BSD and value == "pvclock": # seen on FreeBSD - continue - assert os.path.isabs(nt.path), nt.path - # commented as on Linux we might get - # '/foo/bar (deleted)' - # assert os.path.exists(nt.path), nt.path - elif fname == 'addr': - assert value, repr(value) - elif fname == 'perms': - if not WINDOWS: - assert value, repr(value) - else: - assert isinstance(value, int) - assert value >= 0 - - def num_handles(self, ret, info): - assert isinstance(ret, int) - assert ret >= 0 - - def nice(self, ret, info): - assert isinstance(ret, int) - if POSIX: - assert -20 <= ret <= 20, ret - else: - priorities = [ - getattr(psutil, x) - for x in dir(psutil) - if x.endswith('_PRIORITY_CLASS') - ] - assert ret in priorities - assert isinstance(ret, enum.IntEnum) - - def num_ctx_switches(self, ret, info): - assert is_namedtuple(ret) - for value in ret: - assert isinstance(value, int) - assert value >= 0 - - def rlimit(self, ret, info): - assert isinstance(ret, tuple) - assert len(ret) == 2 - assert ret[0] >= -1 - assert ret[1] >= -1 - - def environ(self, ret, info): - assert isinstance(ret, dict) - for k, v in ret.items(): - assert isinstance(k, str) - assert isinstance(v, str) - - -class TestPidsRange(PsutilTestCase): - """Given pid_exists() return value for a range of PIDs which may or - may not exist, make sure that psutil.Process() and psutil.pids() - agree with pid_exists(). This guarantees that the 3 APIs are all - consistent with each other. See: - https://github.com/giampaolo/psutil/issues/2359 - - XXX - Note about Windows: it turns out there are some "hidden" PIDs - which are not returned by psutil.pids() and are also not revealed - by taskmgr.exe and ProcessHacker, still they can be instantiated by - psutil.Process() and queried. One of such PIDs is "conhost.exe". - Running as_dict() for it reveals that some Process() APIs - erroneously raise NoSuchProcess, so we know we have problem there. - Let's ignore this for now, since it's quite a corner case (who even - imagined hidden PIDs existed on Windows?). - """ - - def setUp(self): - psutil._set_debug(False) - - def tearDown(self): - psutil._set_debug(True) - - def test_it(self): - def is_linux_tid(pid): - try: - f = open(f"/proc/{pid}/status", "rb") # noqa: SIM115 - except FileNotFoundError: - return False - else: - with f: - for line in f: - if line.startswith(b"Tgid:"): - tgid = int(line.split()[1]) - # If tgid and pid are different then we're - # dealing with a process TID. - return tgid != pid - raise ValueError("'Tgid' line not found") - - def check(pid): - # In case of failure retry up to 3 times in order to avoid - # race conditions, especially when running in a CI - # environment where PIDs may appear and disappear at any - # time. - x = 3 - while True: - exists = psutil.pid_exists(pid) - try: - if exists: - psutil.Process(pid) - if not WINDOWS: # see docstring - assert pid in psutil.pids() - else: - # On OpenBSD thread IDs can be instantiated, - # and oneshot() succeeds, but other APIs fail - # with EINVAL. - if not OPENBSD: - with pytest.raises(psutil.NoSuchProcess): - psutil.Process(pid) - if not WINDOWS: # see docstring - assert pid not in psutil.pids() - except (psutil.Error, AssertionError): - x -= 1 - if x == 0: - raise - else: - return - - for pid in range(1, 3000): - if LINUX and is_linux_tid(pid): - # On Linux a TID (thread ID) can be passed to the - # Process class and is querable like a PID (process - # ID). Skip it. - continue - with self.subTest(pid=pid): - check(pid) diff --git a/PortablePython/Lib/site-packages/psutil/tests/test_scripts.py b/PortablePython/Lib/site-packages/psutil/tests/test_scripts.py deleted file mode 100644 index de0ad2a..0000000 --- a/PortablePython/Lib/site-packages/psutil/tests/test_scripts.py +++ /dev/null @@ -1,240 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -"""Test various scripts.""" - -import ast -import os -import shutil -import stat -import subprocess - -import pytest - -from psutil import POSIX -from psutil import WINDOWS -from psutil.tests import CI_TESTING -from psutil.tests import HAS_BATTERY -from psutil.tests import HAS_MEMORY_MAPS -from psutil.tests import HAS_SENSORS_BATTERY -from psutil.tests import HAS_SENSORS_FANS -from psutil.tests import HAS_SENSORS_TEMPERATURES -from psutil.tests import PYTHON_EXE -from psutil.tests import PYTHON_EXE_ENV -from psutil.tests import ROOT_DIR -from psutil.tests import SCRIPTS_DIR -from psutil.tests import PsutilTestCase -from psutil.tests import import_module_by_path -from psutil.tests import psutil -from psutil.tests import sh - - -INTERNAL_SCRIPTS_DIR = os.path.join(SCRIPTS_DIR, "internal") -SETUP_PY = os.path.join(ROOT_DIR, 'setup.py') - - -# =================================================================== -# --- Tests scripts in scripts/ directory -# =================================================================== - - -@pytest.mark.skipif( - CI_TESTING and not os.path.exists(SCRIPTS_DIR), - reason="can't find scripts/ directory", -) -class TestExampleScripts(PsutilTestCase): - @staticmethod - def assert_stdout(exe, *args, **kwargs): - kwargs.setdefault("env", PYTHON_EXE_ENV) - exe = os.path.join(SCRIPTS_DIR, exe) - cmd = [PYTHON_EXE, exe] - for arg in args: - cmd.append(arg) - try: - out = sh(cmd, **kwargs).strip() - except RuntimeError as err: - if 'AccessDenied' in str(err): - return str(err) - else: - raise - assert out, out - return out - - @staticmethod - def assert_syntax(exe): - exe = os.path.join(SCRIPTS_DIR, exe) - with open(exe, encoding="utf8") as f: - src = f.read() - ast.parse(src) - - def test_coverage(self): - # make sure all example scripts have a test method defined - meths = dir(self) - for name in os.listdir(SCRIPTS_DIR): - if name.endswith('.py'): - if 'test_' + os.path.splitext(name)[0] not in meths: - # self.assert_stdout(name) - raise self.fail( - "no test defined for" - f" {os.path.join(SCRIPTS_DIR, name)!r} script" - ) - - @pytest.mark.skipif(not POSIX, reason="POSIX only") - def test_executable(self): - for root, dirs, files in os.walk(SCRIPTS_DIR): - for file in files: - if file.endswith('.py'): - path = os.path.join(root, file) - if not stat.S_IXUSR & os.stat(path)[stat.ST_MODE]: - raise self.fail(f"{path!r} is not executable") - - def test_disk_usage(self): - self.assert_stdout('disk_usage.py') - - def test_free(self): - self.assert_stdout('free.py') - - def test_meminfo(self): - self.assert_stdout('meminfo.py') - - def test_procinfo(self): - self.assert_stdout('procinfo.py', str(os.getpid())) - - @pytest.mark.skipif(CI_TESTING and not psutil.users(), reason="no users") - def test_who(self): - self.assert_stdout('who.py') - - def test_ps(self): - self.assert_stdout('ps.py') - - def test_pstree(self): - self.assert_stdout('pstree.py') - - def test_netstat(self): - self.assert_stdout('netstat.py') - - def test_ifconfig(self): - self.assert_stdout('ifconfig.py') - - @pytest.mark.skipif(not HAS_MEMORY_MAPS, reason="not supported") - def test_pmap(self): - self.assert_stdout('pmap.py', str(os.getpid())) - - def test_procsmem(self): - if 'uss' not in psutil.Process().memory_full_info()._fields: - raise pytest.skip("not supported") - self.assert_stdout('procsmem.py') - - def test_killall(self): - self.assert_syntax('killall.py') - - def test_nettop(self): - self.assert_syntax('nettop.py') - - def test_top(self): - self.assert_syntax('top.py') - - def test_iotop(self): - self.assert_syntax('iotop.py') - - def test_pidof(self): - output = self.assert_stdout('pidof.py', psutil.Process().name()) - assert str(os.getpid()) in output - - @pytest.mark.skipif(not WINDOWS, reason="WINDOWS only") - def test_winservices(self): - self.assert_stdout('winservices.py') - - def test_cpu_distribution(self): - self.assert_syntax('cpu_distribution.py') - - @pytest.mark.skipif(not HAS_SENSORS_TEMPERATURES, reason="not supported") - def test_temperatures(self): - if not psutil.sensors_temperatures(): - raise pytest.skip("no temperatures") - self.assert_stdout('temperatures.py') - - @pytest.mark.skipif(not HAS_SENSORS_FANS, reason="not supported") - def test_fans(self): - if not psutil.sensors_fans(): - raise pytest.skip("no fans") - self.assert_stdout('fans.py') - - @pytest.mark.skipif(not HAS_SENSORS_BATTERY, reason="not supported") - @pytest.mark.skipif(not HAS_BATTERY, reason="no battery") - def test_battery(self): - self.assert_stdout('battery.py') - - @pytest.mark.skipif(not HAS_SENSORS_BATTERY, reason="not supported") - @pytest.mark.skipif(not HAS_BATTERY, reason="no battery") - def test_sensors(self): - self.assert_stdout('sensors.py') - - -# =================================================================== -# --- Tests scripts in scripts/internal/ directory -# =================================================================== - - -@pytest.mark.skipif( - CI_TESTING and not os.path.exists(INTERNAL_SCRIPTS_DIR), - reason="can't find scripts/internal/ directory", -) -class TestInternalScripts(PsutilTestCase): - @staticmethod - def ls(): - for name in os.listdir(INTERNAL_SCRIPTS_DIR): - if name.endswith(".py"): - yield os.path.join(INTERNAL_SCRIPTS_DIR, name) - - def test_syntax_all(self): - for path in self.ls(): - with open(path, encoding="utf8") as f: - data = f.read() - ast.parse(data) - - @pytest.mark.skipif(CI_TESTING, reason="not on CI") - def test_import_all(self): - for path in self.ls(): - try: - import_module_by_path(path) - except SystemExit: - pass - - -# =================================================================== -# --- Tests for setup.py script -# =================================================================== - - -@pytest.mark.skipif( - CI_TESTING and not os.path.exists(SETUP_PY), reason="can't find setup.py" -) -class TestSetupScript(PsutilTestCase): - def test_invocation(self): - module = import_module_by_path(SETUP_PY) - with pytest.raises(SystemExit): - module.setup() - assert module.get_version() == psutil.__version__ - - @pytest.mark.skipif( - not shutil.which("python2.7"), reason="python2.7 not installed" - ) - def test_python2(self): - # There's a duplicate of this test in scripts/internal - # directory, which is only executed by CI. We replicate it here - # to run it when developing locally. - p = subprocess.Popen( - [shutil.which("python2.7"), SETUP_PY], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - universal_newlines=True, - ) - stdout, stderr = p.communicate() - assert p.wait() == 1 - assert not stdout - assert "psutil no longer supports Python 2.7" in stderr - assert "Latest version supporting Python 2.7 is" in stderr diff --git a/PortablePython/Lib/site-packages/psutil/tests/test_sunos.py b/PortablePython/Lib/site-packages/psutil/tests/test_sunos.py deleted file mode 100644 index b5d9d35..0000000 --- a/PortablePython/Lib/site-packages/psutil/tests/test_sunos.py +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -"""Sun OS specific tests.""" - -import os - -import psutil -from psutil import SUNOS -from psutil.tests import PsutilTestCase -from psutil.tests import pytest -from psutil.tests import sh - - -@pytest.mark.skipif(not SUNOS, reason="SUNOS only") -class SunOSSpecificTestCase(PsutilTestCase): - def test_swap_memory(self): - out = sh(f"env PATH=/usr/sbin:/sbin:{os.environ['PATH']} swap -l") - lines = out.strip().split('\n')[1:] - if not lines: - raise ValueError('no swap device(s) configured') - total = free = 0 - for line in lines: - fields = line.split() - total = int(fields[3]) * 512 - free = int(fields[4]) * 512 - used = total - free - - psutil_swap = psutil.swap_memory() - assert psutil_swap.total == total - assert psutil_swap.used == used - assert psutil_swap.free == free - - def test_cpu_count(self): - out = sh("/usr/sbin/psrinfo") - assert psutil.cpu_count() == len(out.split('\n')) diff --git a/PortablePython/Lib/site-packages/psutil/tests/test_system.py b/PortablePython/Lib/site-packages/psutil/tests/test_system.py deleted file mode 100644 index b961e1f..0000000 --- a/PortablePython/Lib/site-packages/psutil/tests/test_system.py +++ /dev/null @@ -1,979 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -"""Tests for system APIS.""" - -import datetime -import enum -import errno -import os -import platform -import pprint -import shutil -import signal -import socket -import sys -import time -from unittest import mock - -import psutil -from psutil import AIX -from psutil import BSD -from psutil import FREEBSD -from psutil import LINUX -from psutil import MACOS -from psutil import NETBSD -from psutil import OPENBSD -from psutil import POSIX -from psutil import SUNOS -from psutil import WINDOWS -from psutil._common import broadcast_addr -from psutil.tests import AARCH64 -from psutil.tests import ASCII_FS -from psutil.tests import CI_TESTING -from psutil.tests import GITHUB_ACTIONS -from psutil.tests import GLOBAL_TIMEOUT -from psutil.tests import HAS_BATTERY -from psutil.tests import HAS_CPU_FREQ -from psutil.tests import HAS_GETLOADAVG -from psutil.tests import HAS_NET_IO_COUNTERS -from psutil.tests import HAS_SENSORS_BATTERY -from psutil.tests import HAS_SENSORS_FANS -from psutil.tests import HAS_SENSORS_TEMPERATURES -from psutil.tests import IS_64BIT -from psutil.tests import MACOS_12PLUS -from psutil.tests import PYPY -from psutil.tests import UNICODE_SUFFIX -from psutil.tests import PsutilTestCase -from psutil.tests import check_net_address -from psutil.tests import pytest -from psutil.tests import retry_on_failure - - -# =================================================================== -# --- System-related API tests -# =================================================================== - - -class TestProcessIter(PsutilTestCase): - def test_pid_presence(self): - assert os.getpid() in [x.pid for x in psutil.process_iter()] - sproc = self.spawn_testproc() - assert sproc.pid in [x.pid for x in psutil.process_iter()] - p = psutil.Process(sproc.pid) - p.kill() - p.wait() - assert sproc.pid not in [x.pid for x in psutil.process_iter()] - - def test_no_duplicates(self): - ls = list(psutil.process_iter()) - assert sorted(ls, key=lambda x: x.pid) == sorted( - set(ls), key=lambda x: x.pid - ) - - def test_emulate_nsp(self): - list(psutil.process_iter()) # populate cache - for x in range(2): - with mock.patch( - 'psutil.Process.as_dict', - side_effect=psutil.NoSuchProcess(os.getpid()), - ): - assert not list(psutil.process_iter(attrs=["cpu_times"])) - psutil.process_iter.cache_clear() # repeat test without cache - - def test_emulate_access_denied(self): - list(psutil.process_iter()) # populate cache - for x in range(2): - with mock.patch( - 'psutil.Process.as_dict', - side_effect=psutil.AccessDenied(os.getpid()), - ): - with pytest.raises(psutil.AccessDenied): - list(psutil.process_iter(attrs=["cpu_times"])) - psutil.process_iter.cache_clear() # repeat test without cache - - def test_attrs(self): - for p in psutil.process_iter(attrs=['pid']): - assert list(p.info.keys()) == ['pid'] - # yield again - for p in psutil.process_iter(attrs=['pid']): - assert list(p.info.keys()) == ['pid'] - with pytest.raises(ValueError): - list(psutil.process_iter(attrs=['foo'])) - with mock.patch( - "psutil._psplatform.Process.cpu_times", - side_effect=psutil.AccessDenied(0, ""), - ) as m: - for p in psutil.process_iter(attrs=["pid", "cpu_times"]): - assert p.info['cpu_times'] is None - assert p.info['pid'] >= 0 - assert m.called - with mock.patch( - "psutil._psplatform.Process.cpu_times", - side_effect=psutil.AccessDenied(0, ""), - ) as m: - flag = object() - for p in psutil.process_iter( - attrs=["pid", "cpu_times"], ad_value=flag - ): - assert p.info['cpu_times'] is flag - assert p.info['pid'] >= 0 - assert m.called - - def test_cache_clear(self): - list(psutil.process_iter()) # populate cache - assert psutil._pmap - psutil.process_iter.cache_clear() - assert not psutil._pmap - - -class TestProcessAPIs(PsutilTestCase): - @pytest.mark.skipif( - PYPY and WINDOWS, - reason="spawn_testproc() unreliable on PYPY + WINDOWS", - ) - def test_wait_procs(self): - def callback(p): - pids.append(p.pid) - - pids = [] - sproc1 = self.spawn_testproc() - sproc2 = self.spawn_testproc() - sproc3 = self.spawn_testproc() - procs = [psutil.Process(x.pid) for x in (sproc1, sproc2, sproc3)] - with pytest.raises(ValueError): - psutil.wait_procs(procs, timeout=-1) - with pytest.raises(TypeError): - psutil.wait_procs(procs, callback=1) - t = time.time() - gone, alive = psutil.wait_procs(procs, timeout=0.01, callback=callback) - - assert time.time() - t < 0.5 - assert not gone - assert len(alive) == 3 - assert not pids - for p in alive: - assert not hasattr(p, 'returncode') - - @retry_on_failure(30) - def test_1(procs, callback): - gone, alive = psutil.wait_procs( - procs, timeout=0.03, callback=callback - ) - assert len(gone) == 1 - assert len(alive) == 2 - return gone, alive - - sproc3.terminate() - gone, alive = test_1(procs, callback) - assert sproc3.pid in [x.pid for x in gone] - if POSIX: - assert gone.pop().returncode == -signal.SIGTERM - else: - assert gone.pop().returncode == 1 - assert pids == [sproc3.pid] - for p in alive: - assert not hasattr(p, 'returncode') - - @retry_on_failure(30) - def test_2(procs, callback): - gone, alive = psutil.wait_procs( - procs, timeout=0.03, callback=callback - ) - assert len(gone) == 3 - assert len(alive) == 0 - return gone, alive - - sproc1.terminate() - sproc2.terminate() - gone, alive = test_2(procs, callback) - assert set(pids) == {sproc1.pid, sproc2.pid, sproc3.pid} - for p in gone: - assert hasattr(p, 'returncode') - - @pytest.mark.skipif( - PYPY and WINDOWS, - reason="spawn_testproc() unreliable on PYPY + WINDOWS", - ) - def test_wait_procs_no_timeout(self): - sproc1 = self.spawn_testproc() - sproc2 = self.spawn_testproc() - sproc3 = self.spawn_testproc() - procs = [psutil.Process(x.pid) for x in (sproc1, sproc2, sproc3)] - for p in procs: - p.terminate() - psutil.wait_procs(procs) - - def test_pid_exists(self): - sproc = self.spawn_testproc() - assert psutil.pid_exists(sproc.pid) - p = psutil.Process(sproc.pid) - p.kill() - p.wait() - assert not psutil.pid_exists(sproc.pid) - assert not psutil.pid_exists(-1) - assert psutil.pid_exists(0) == (0 in psutil.pids()) - - def test_pid_exists_2(self): - pids = psutil.pids() - for pid in pids: - try: - assert psutil.pid_exists(pid) - except AssertionError: - # in case the process disappeared in meantime fail only - # if it is no longer in psutil.pids() - time.sleep(0.1) - assert pid not in psutil.pids() - pids = range(max(pids) + 15000, max(pids) + 16000) - for pid in pids: - assert not psutil.pid_exists(pid) - - -class TestMiscAPIs(PsutilTestCase): - def test_boot_time(self): - bt = psutil.boot_time() - assert isinstance(bt, float) - assert bt > 0 - assert bt < time.time() - - @pytest.mark.skipif( - CI_TESTING and not psutil.users(), reason="unreliable on CI" - ) - def test_users(self): - users = psutil.users() - assert users - for user in users: - with self.subTest(user=user): - assert user.name - assert isinstance(user.name, str) - assert isinstance(user.terminal, (str, type(None))) - if user.host is not None: - assert isinstance(user.host, (str, type(None))) - user.terminal # noqa: B018 - user.host # noqa: B018 - assert user.started > 0.0 - datetime.datetime.fromtimestamp(user.started) - if WINDOWS or OPENBSD: - assert user.pid is None - else: - psutil.Process(user.pid) - - def test_os_constants(self): - names = [ - "POSIX", - "WINDOWS", - "LINUX", - "MACOS", - "FREEBSD", - "OPENBSD", - "NETBSD", - "BSD", - "SUNOS", - ] - for name in names: - assert isinstance(getattr(psutil, name), bool), name - - if os.name == 'posix': - assert psutil.POSIX - assert not psutil.WINDOWS - names.remove("POSIX") - if "linux" in sys.platform.lower(): - assert psutil.LINUX - names.remove("LINUX") - elif "bsd" in sys.platform.lower(): - assert psutil.BSD - assert [psutil.FREEBSD, psutil.OPENBSD, psutil.NETBSD].count( - True - ) == 1 - names.remove("BSD") - names.remove("FREEBSD") - names.remove("OPENBSD") - names.remove("NETBSD") - elif ( - "sunos" in sys.platform.lower() - or "solaris" in sys.platform.lower() - ): - assert psutil.SUNOS - names.remove("SUNOS") - elif "darwin" in sys.platform.lower(): - assert psutil.MACOS - names.remove("MACOS") - else: - assert psutil.WINDOWS - assert not psutil.POSIX - names.remove("WINDOWS") - - # assert all other constants are set to False - for name in names: - assert not getattr(psutil, name), name - - -class TestMemoryAPIs(PsutilTestCase): - def test_virtual_memory(self): - mem = psutil.virtual_memory() - assert mem.total > 0, mem - assert mem.available > 0, mem - assert 0 <= mem.percent <= 100, mem - assert mem.used > 0, mem - assert mem.free >= 0, mem - for name in mem._fields: - value = getattr(mem, name) - if name != 'percent': - assert isinstance(value, int) - if name != 'total': - if not value >= 0: - raise self.fail(f"{name!r} < 0 ({value})") - if value > mem.total: - raise self.fail( - f"{name!r} > total (total={mem.total}, {name}={value})" - ) - - def test_swap_memory(self): - mem = psutil.swap_memory() - assert mem._fields == ( - 'total', - 'used', - 'free', - 'percent', - 'sin', - 'sout', - ) - - assert mem.total >= 0, mem - assert mem.used >= 0, mem - if mem.total > 0: - # likely a system with no swap partition - assert mem.free > 0, mem - else: - assert mem.free == 0, mem - assert 0 <= mem.percent <= 100, mem - assert mem.sin >= 0, mem - assert mem.sout >= 0, mem - - -class TestCpuAPIs(PsutilTestCase): - def test_cpu_count_logical(self): - logical = psutil.cpu_count() - assert logical is not None - assert logical == len(psutil.cpu_times(percpu=True)) - assert logical >= 1 - - if os.path.exists("/proc/cpuinfo"): - with open("/proc/cpuinfo") as fd: - cpuinfo_data = fd.read() - if "physical id" not in cpuinfo_data: - raise pytest.skip("cpuinfo doesn't include physical id") - - def test_cpu_count_cores(self): - logical = psutil.cpu_count() - cores = psutil.cpu_count(logical=False) - if cores is None: - raise pytest.skip("cpu_count_cores() is None") - if WINDOWS and sys.getwindowsversion()[:2] <= (6, 1): # <= Vista - assert cores is None - else: - assert cores >= 1 - assert logical >= cores - - def test_cpu_count_none(self): - # https://github.com/giampaolo/psutil/issues/1085 - for val in (-1, 0, None): - with mock.patch( - 'psutil._psplatform.cpu_count_logical', return_value=val - ) as m: - assert psutil.cpu_count() is None - assert m.called - with mock.patch( - 'psutil._psplatform.cpu_count_cores', return_value=val - ) as m: - assert psutil.cpu_count(logical=False) is None - assert m.called - - def test_cpu_times(self): - # Check type, value >= 0, str(). - total = 0 - times = psutil.cpu_times() - sum(times) - for cp_time in times: - assert isinstance(cp_time, float) - assert cp_time >= 0.0 - total += cp_time - assert round(abs(total - sum(times)), 6) == 0 - str(times) - # CPU times are always supposed to increase over time - # or at least remain the same and that's because time - # cannot go backwards. - # Surprisingly sometimes this might not be the case (at - # least on Windows and Linux), see: - # https://github.com/giampaolo/psutil/issues/392 - # https://github.com/giampaolo/psutil/issues/645 - # if not WINDOWS: - # last = psutil.cpu_times() - # for x in range(100): - # new = psutil.cpu_times() - # for field in new._fields: - # new_t = getattr(new, field) - # last_t = getattr(last, field) - # self.assertGreaterEqual( - # new_t, last_t, - # msg="{} {}".format(new_t, last_t)) - # last = new - - def test_cpu_times_time_increases(self): - # Make sure time increases between calls. - t1 = sum(psutil.cpu_times()) - stop_at = time.time() + GLOBAL_TIMEOUT - while time.time() < stop_at: - t2 = sum(psutil.cpu_times()) - if t2 > t1: - return - raise self.fail("time remained the same") - - def test_per_cpu_times(self): - # Check type, value >= 0, str(). - for times in psutil.cpu_times(percpu=True): - total = 0 - sum(times) - for cp_time in times: - assert isinstance(cp_time, float) - assert cp_time >= 0.0 - total += cp_time - assert round(abs(total - sum(times)), 6) == 0 - str(times) - assert len(psutil.cpu_times(percpu=True)[0]) == len( - psutil.cpu_times(percpu=False) - ) - - # Note: in theory CPU times are always supposed to increase over - # time or remain the same but never go backwards. In practice - # sometimes this is not the case. - # This issue seemd to be afflict Windows: - # https://github.com/giampaolo/psutil/issues/392 - # ...but it turns out also Linux (rarely) behaves the same. - # last = psutil.cpu_times(percpu=True) - # for x in range(100): - # new = psutil.cpu_times(percpu=True) - # for index in range(len(new)): - # newcpu = new[index] - # lastcpu = last[index] - # for field in newcpu._fields: - # new_t = getattr(newcpu, field) - # last_t = getattr(lastcpu, field) - # self.assertGreaterEqual( - # new_t, last_t, msg="{} {}".format(lastcpu, newcpu)) - # last = new - - def test_per_cpu_times_2(self): - # Simulate some work load then make sure time have increased - # between calls. - tot1 = psutil.cpu_times(percpu=True) - giveup_at = time.time() + GLOBAL_TIMEOUT - while True: - if time.time() >= giveup_at: - return self.fail("timeout") - tot2 = psutil.cpu_times(percpu=True) - for t1, t2 in zip(tot1, tot2): - t1, t2 = psutil._cpu_busy_time(t1), psutil._cpu_busy_time(t2) - difference = t2 - t1 - if difference >= 0.05: - return None - - @pytest.mark.skipif( - CI_TESTING and OPENBSD, reason="unreliable on OPENBSD + CI" - ) - @retry_on_failure(30) - def test_cpu_times_comparison(self): - # Make sure the sum of all per cpu times is almost equal to - # base "one cpu" times. On OpenBSD the sum of per-CPUs is - # higher for some reason. - base = psutil.cpu_times() - per_cpu = psutil.cpu_times(percpu=True) - summed_values = base._make([sum(num) for num in zip(*per_cpu)]) - for field in base._fields: - with self.subTest(field=field, base=base, per_cpu=per_cpu): - assert ( - abs(getattr(base, field) - getattr(summed_values, field)) - < 2 - ) - - def _test_cpu_percent(self, percent, last_ret, new_ret): - try: - assert isinstance(percent, float) - assert percent >= 0.0 - assert percent <= 100.0 * psutil.cpu_count() - except AssertionError as err: - raise AssertionError( - "\n{}\nlast={}\nnew={}".format( - err, pprint.pformat(last_ret), pprint.pformat(new_ret) - ) - ) - - def test_cpu_percent(self): - last = psutil.cpu_percent(interval=0.001) - for _ in range(100): - new = psutil.cpu_percent(interval=None) - self._test_cpu_percent(new, last, new) - last = new - with pytest.raises(ValueError): - psutil.cpu_percent(interval=-1) - - def test_per_cpu_percent(self): - last = psutil.cpu_percent(interval=0.001, percpu=True) - assert len(last) == psutil.cpu_count() - for _ in range(100): - new = psutil.cpu_percent(interval=None, percpu=True) - for percent in new: - self._test_cpu_percent(percent, last, new) - last = new - with pytest.raises(ValueError): - psutil.cpu_percent(interval=-1, percpu=True) - - def test_cpu_times_percent(self): - last = psutil.cpu_times_percent(interval=0.001) - for _ in range(100): - new = psutil.cpu_times_percent(interval=None) - for percent in new: - self._test_cpu_percent(percent, last, new) - self._test_cpu_percent(sum(new), last, new) - last = new - with pytest.raises(ValueError): - psutil.cpu_times_percent(interval=-1) - - def test_per_cpu_times_percent(self): - last = psutil.cpu_times_percent(interval=0.001, percpu=True) - assert len(last) == psutil.cpu_count() - for _ in range(100): - new = psutil.cpu_times_percent(interval=None, percpu=True) - for cpu in new: - for percent in cpu: - self._test_cpu_percent(percent, last, new) - self._test_cpu_percent(sum(cpu), last, new) - last = new - - def test_per_cpu_times_percent_negative(self): - # see: https://github.com/giampaolo/psutil/issues/645 - psutil.cpu_times_percent(percpu=True) - zero_times = [ - x._make([0 for x in range(len(x._fields))]) - for x in psutil.cpu_times(percpu=True) - ] - with mock.patch('psutil.cpu_times', return_value=zero_times): - for cpu in psutil.cpu_times_percent(percpu=True): - for percent in cpu: - self._test_cpu_percent(percent, None, None) - - def test_cpu_stats(self): - # Tested more extensively in per-platform test modules. - infos = psutil.cpu_stats() - assert infos._fields == ( - 'ctx_switches', - 'interrupts', - 'soft_interrupts', - 'syscalls', - ) - for name in infos._fields: - value = getattr(infos, name) - assert value >= 0 - # on AIX, ctx_switches is always 0 - if not AIX and name in {'ctx_switches', 'interrupts'}: - assert value > 0 - - # TODO: remove this once 1892 is fixed - @pytest.mark.skipif( - MACOS and platform.machine() == 'arm64', reason="skipped due to #1892" - ) - @pytest.mark.skipif(not HAS_CPU_FREQ, reason="not supported") - def test_cpu_freq(self): - def check_ls(ls): - for nt in ls: - assert nt._fields == ('current', 'min', 'max') - if nt.max != 0.0: - assert nt.current <= nt.max - for name in nt._fields: - value = getattr(nt, name) - assert isinstance(value, (int, float)) - assert value >= 0 - - ls = psutil.cpu_freq(percpu=True) - if (FREEBSD or AARCH64) and not ls: - raise pytest.skip( - "returns empty list on FreeBSD and Linux aarch64" - ) - - assert ls, ls - check_ls([psutil.cpu_freq(percpu=False)]) - - if LINUX: - assert len(ls) == psutil.cpu_count() - - @pytest.mark.skipif(not HAS_GETLOADAVG, reason="not supported") - def test_getloadavg(self): - loadavg = psutil.getloadavg() - assert len(loadavg) == 3 - for load in loadavg: - assert isinstance(load, float) - assert load >= 0.0 - - -class TestDiskAPIs(PsutilTestCase): - @pytest.mark.skipif( - PYPY and not IS_64BIT, reason="unreliable on PYPY32 + 32BIT" - ) - def test_disk_usage(self): - usage = psutil.disk_usage(os.getcwd()) - assert usage._fields == ('total', 'used', 'free', 'percent') - - assert usage.total > 0, usage - assert usage.used > 0, usage - assert usage.free > 0, usage - assert usage.total > usage.used, usage - assert usage.total > usage.free, usage - assert 0 <= usage.percent <= 100, usage.percent - if hasattr(shutil, 'disk_usage'): - # py >= 3.3, see: http://bugs.python.org/issue12442 - shutil_usage = shutil.disk_usage(os.getcwd()) - tolerance = 5 * 1024 * 1024 # 5MB - assert usage.total == shutil_usage.total - assert abs(usage.free - shutil_usage.free) < tolerance - if not MACOS_12PLUS: - # see https://github.com/giampaolo/psutil/issues/2147 - assert abs(usage.used - shutil_usage.used) < tolerance - - # if path does not exist OSError ENOENT is expected across - # all platforms - fname = self.get_testfn() - with pytest.raises(FileNotFoundError): - psutil.disk_usage(fname) - - @pytest.mark.skipif(not ASCII_FS, reason="not an ASCII fs") - def test_disk_usage_unicode(self): - # See: https://github.com/giampaolo/psutil/issues/416 - with pytest.raises(UnicodeEncodeError): - psutil.disk_usage(UNICODE_SUFFIX) - - def test_disk_usage_bytes(self): - psutil.disk_usage(b'.') - - def test_disk_partitions(self): - def check_ntuple(nt): - assert isinstance(nt.device, str) - assert isinstance(nt.mountpoint, str) - assert isinstance(nt.fstype, str) - assert isinstance(nt.opts, str) - - # all = False - ls = psutil.disk_partitions(all=False) - assert ls - for disk in ls: - check_ntuple(disk) - if WINDOWS and 'cdrom' in disk.opts: - continue - if not POSIX: - assert os.path.exists(disk.device), disk - else: - # we cannot make any assumption about this, see: - # http://goo.gl/p9c43 - disk.device # noqa: B018 - # on modern systems mount points can also be files - assert os.path.exists(disk.mountpoint), disk - assert disk.fstype, disk - - # all = True - ls = psutil.disk_partitions(all=True) - assert ls - for disk in psutil.disk_partitions(all=True): - check_ntuple(disk) - if not WINDOWS and disk.mountpoint: - try: - os.stat(disk.mountpoint) - except OSError as err: - if GITHUB_ACTIONS and MACOS and err.errno == errno.EIO: - continue - # http://mail.python.org/pipermail/python-dev/ - # 2012-June/120787.html - if err.errno not in {errno.EPERM, errno.EACCES}: - raise - else: - assert os.path.exists(disk.mountpoint), disk - - # --- - - def find_mount_point(path): - path = os.path.abspath(path) - while not os.path.ismount(path): - path = os.path.dirname(path) - return path.lower() - - mount = find_mount_point(__file__) - mounts = [ - x.mountpoint.lower() - for x in psutil.disk_partitions(all=True) - if x.mountpoint - ] - assert mount in mounts - - @pytest.mark.skipif( - LINUX and not os.path.exists('/proc/diskstats'), - reason="/proc/diskstats not available on this linux version", - ) - @pytest.mark.skipif( - CI_TESTING and not psutil.disk_io_counters(), reason="unreliable on CI" - ) # no visible disks - def test_disk_io_counters(self): - def check_ntuple(nt): - assert nt[0] == nt.read_count - assert nt[1] == nt.write_count - assert nt[2] == nt.read_bytes - assert nt[3] == nt.write_bytes - if not (OPENBSD or NETBSD): - assert nt[4] == nt.read_time - assert nt[5] == nt.write_time - if LINUX: - assert nt[6] == nt.read_merged_count - assert nt[7] == nt.write_merged_count - assert nt[8] == nt.busy_time - elif FREEBSD: - assert nt[6] == nt.busy_time - for name in nt._fields: - assert getattr(nt, name) >= 0, nt - - ret = psutil.disk_io_counters(perdisk=False) - assert ret is not None, "no disks on this system?" - check_ntuple(ret) - ret = psutil.disk_io_counters(perdisk=True) - # make sure there are no duplicates - assert len(ret) == len(set(ret)) - for key in ret: - assert key, key - check_ntuple(ret[key]) - - def test_disk_io_counters_no_disks(self): - # Emulate a case where no disks are installed, see: - # https://github.com/giampaolo/psutil/issues/1062 - with mock.patch( - 'psutil._psplatform.disk_io_counters', return_value={} - ) as m: - assert psutil.disk_io_counters(perdisk=False) is None - assert psutil.disk_io_counters(perdisk=True) == {} - assert m.called - - -class TestNetAPIs(PsutilTestCase): - @pytest.mark.skipif(not HAS_NET_IO_COUNTERS, reason="not supported") - def test_net_io_counters(self): - def check_ntuple(nt): - assert nt[0] == nt.bytes_sent - assert nt[1] == nt.bytes_recv - assert nt[2] == nt.packets_sent - assert nt[3] == nt.packets_recv - assert nt[4] == nt.errin - assert nt[5] == nt.errout - assert nt[6] == nt.dropin - assert nt[7] == nt.dropout - assert nt.bytes_sent >= 0, nt - assert nt.bytes_recv >= 0, nt - assert nt.packets_sent >= 0, nt - assert nt.packets_recv >= 0, nt - assert nt.errin >= 0, nt - assert nt.errout >= 0, nt - assert nt.dropin >= 0, nt - assert nt.dropout >= 0, nt - - ret = psutil.net_io_counters(pernic=False) - check_ntuple(ret) - ret = psutil.net_io_counters(pernic=True) - assert ret != [] - for key in ret: - assert key - assert isinstance(key, str) - check_ntuple(ret[key]) - - @pytest.mark.skipif(not HAS_NET_IO_COUNTERS, reason="not supported") - def test_net_io_counters_no_nics(self): - # Emulate a case where no NICs are installed, see: - # https://github.com/giampaolo/psutil/issues/1062 - with mock.patch( - 'psutil._psplatform.net_io_counters', return_value={} - ) as m: - assert psutil.net_io_counters(pernic=False) is None - assert psutil.net_io_counters(pernic=True) == {} - assert m.called - - def test_net_if_addrs(self): - nics = psutil.net_if_addrs() - assert nics, nics - - nic_stats = psutil.net_if_stats() - - # Not reliable on all platforms (net_if_addrs() reports more - # interfaces). - # self.assertEqual(sorted(nics.keys()), - # sorted(psutil.net_io_counters(pernic=True).keys())) - - families = {socket.AF_INET, socket.AF_INET6, psutil.AF_LINK} - for nic, addrs in nics.items(): - assert isinstance(nic, str) - assert len(set(addrs)) == len(addrs) - for addr in addrs: - assert isinstance(addr.family, int) - assert isinstance(addr.address, str) - assert isinstance(addr.netmask, (str, type(None))) - assert isinstance(addr.broadcast, (str, type(None))) - assert addr.family in families - assert isinstance(addr.family, enum.IntEnum) - if nic_stats[nic].isup: - # Do not test binding to addresses of interfaces - # that are down - if addr.family == socket.AF_INET: - with socket.socket(addr.family) as s: - s.bind((addr.address, 0)) - elif addr.family == socket.AF_INET6: - info = socket.getaddrinfo( - addr.address, - 0, - socket.AF_INET6, - socket.SOCK_STREAM, - 0, - socket.AI_PASSIVE, - )[0] - af, socktype, proto, _canonname, sa = info - with socket.socket(af, socktype, proto) as s: - s.bind(sa) - for ip in ( - addr.address, - addr.netmask, - addr.broadcast, - addr.ptp, - ): - if ip is not None: - # TODO: skip AF_INET6 for now because I get: - # AddressValueError: Only hex digits permitted in - # u'c6f3%lxcbr0' in u'fe80::c8e0:fff:fe54:c6f3%lxcbr0' - if addr.family != socket.AF_INET6: - check_net_address(ip, addr.family) - # broadcast and ptp addresses are mutually exclusive - if addr.broadcast: - assert addr.ptp is None - elif addr.ptp: - assert addr.broadcast is None - - # check broadcast address - if ( - addr.broadcast - and addr.netmask - and addr.family in {socket.AF_INET, socket.AF_INET6} - ): - assert addr.broadcast == broadcast_addr(addr) - - if BSD or MACOS or SUNOS: - if hasattr(socket, "AF_LINK"): - assert psutil.AF_LINK == socket.AF_LINK - elif LINUX: - assert psutil.AF_LINK == socket.AF_PACKET - elif WINDOWS: - assert psutil.AF_LINK == -1 - - def test_net_if_addrs_mac_null_bytes(self): - # Simulate that the underlying C function returns an incomplete - # MAC address. psutil is supposed to fill it with null bytes. - # https://github.com/giampaolo/psutil/issues/786 - if POSIX: - ret = [('em1', psutil.AF_LINK, '06:3d:29', None, None, None)] - else: - ret = [('em1', -1, '06-3d-29', None, None, None)] - with mock.patch( - 'psutil._psplatform.net_if_addrs', return_value=ret - ) as m: - addr = psutil.net_if_addrs()['em1'][0] - assert m.called - if POSIX: - assert addr.address == '06:3d:29:00:00:00' - else: - assert addr.address == '06-3d-29-00-00-00' - - def test_net_if_stats(self): - nics = psutil.net_if_stats() - assert nics, nics - all_duplexes = ( - psutil.NIC_DUPLEX_FULL, - psutil.NIC_DUPLEX_HALF, - psutil.NIC_DUPLEX_UNKNOWN, - ) - for name, stats in nics.items(): - assert isinstance(name, str) - isup, duplex, speed, mtu, flags = stats - assert isinstance(isup, bool) - assert duplex in all_duplexes - assert duplex in all_duplexes - assert speed >= 0 - assert mtu >= 0 - assert isinstance(flags, str) - - @pytest.mark.skipif( - not (LINUX or BSD or MACOS), reason="LINUX or BSD or MACOS specific" - ) - def test_net_if_stats_enodev(self): - # See: https://github.com/giampaolo/psutil/issues/1279 - with mock.patch( - 'psutil._psutil_posix.net_if_mtu', - side_effect=OSError(errno.ENODEV, ""), - ) as m: - ret = psutil.net_if_stats() - assert ret == {} - assert m.called - - -class TestSensorsAPIs(PsutilTestCase): - @pytest.mark.skipif(not HAS_SENSORS_TEMPERATURES, reason="not supported") - def test_sensors_temperatures(self): - temps = psutil.sensors_temperatures() - for name, entries in temps.items(): - assert isinstance(name, str) - for entry in entries: - assert isinstance(entry.label, str) - if entry.current is not None: - assert entry.current >= 0 - if entry.high is not None: - assert entry.high >= 0 - if entry.critical is not None: - assert entry.critical >= 0 - - @pytest.mark.skipif(not HAS_SENSORS_TEMPERATURES, reason="not supported") - def test_sensors_temperatures_fahreneit(self): - d = {'coretemp': [('label', 50.0, 60.0, 70.0)]} - with mock.patch( - "psutil._psplatform.sensors_temperatures", return_value=d - ) as m: - temps = psutil.sensors_temperatures(fahrenheit=True)['coretemp'][0] - assert m.called - assert temps.current == 122.0 - assert temps.high == 140.0 - assert temps.critical == 158.0 - - @pytest.mark.skipif(not HAS_SENSORS_BATTERY, reason="not supported") - @pytest.mark.skipif(not HAS_BATTERY, reason="no battery") - def test_sensors_battery(self): - ret = psutil.sensors_battery() - assert ret.percent >= 0 - assert ret.percent <= 100 - if ret.secsleft not in { - psutil.POWER_TIME_UNKNOWN, - psutil.POWER_TIME_UNLIMITED, - }: - assert ret.secsleft >= 0 - elif ret.secsleft == psutil.POWER_TIME_UNLIMITED: - assert ret.power_plugged - assert isinstance(ret.power_plugged, bool) - - @pytest.mark.skipif(not HAS_SENSORS_FANS, reason="not supported") - def test_sensors_fans(self): - fans = psutil.sensors_fans() - for name, entries in fans.items(): - assert isinstance(name, str) - for entry in entries: - assert isinstance(entry.label, str) - assert isinstance(entry.current, int) - assert entry.current >= 0 diff --git a/PortablePython/Lib/site-packages/psutil/tests/test_testutils.py b/PortablePython/Lib/site-packages/psutil/tests/test_testutils.py deleted file mode 100644 index 6db66e5..0000000 --- a/PortablePython/Lib/site-packages/psutil/tests/test_testutils.py +++ /dev/null @@ -1,577 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -"""Tests for testing utils (psutil.tests namespace).""" - -import collections -import errno -import os -import socket -import stat -import subprocess -import textwrap -import unittest -import warnings -from unittest import mock - -import psutil -import psutil.tests -from psutil import FREEBSD -from psutil import NETBSD -from psutil import POSIX -from psutil._common import open_binary -from psutil._common import open_text -from psutil._common import supports_ipv6 -from psutil.tests import CI_TESTING -from psutil.tests import COVERAGE -from psutil.tests import HAS_NET_CONNECTIONS_UNIX -from psutil.tests import HERE -from psutil.tests import PYTHON_EXE -from psutil.tests import PYTHON_EXE_ENV -from psutil.tests import PsutilTestCase -from psutil.tests import TestMemoryLeak -from psutil.tests import bind_socket -from psutil.tests import bind_unix_socket -from psutil.tests import call_until -from psutil.tests import chdir -from psutil.tests import create_sockets -from psutil.tests import fake_pytest -from psutil.tests import filter_proc_net_connections -from psutil.tests import get_free_port -from psutil.tests import is_namedtuple -from psutil.tests import process_namespace -from psutil.tests import pytest -from psutil.tests import reap_children -from psutil.tests import retry -from psutil.tests import retry_on_failure -from psutil.tests import safe_mkdir -from psutil.tests import safe_rmpath -from psutil.tests import system_namespace -from psutil.tests import tcp_socketpair -from psutil.tests import terminate -from psutil.tests import unix_socketpair -from psutil.tests import wait_for_file -from psutil.tests import wait_for_pid - - -# =================================================================== -# --- Unit tests for test utilities. -# =================================================================== - - -class TestRetryDecorator(PsutilTestCase): - @mock.patch('time.sleep') - def test_retry_success(self, sleep): - # Fail 3 times out of 5; make sure the decorated fun returns. - - @retry(retries=5, interval=1, logfun=None) - def foo(): - while queue: - queue.pop() - 1 / 0 # noqa: B018 - return 1 - - queue = list(range(3)) - assert foo() == 1 - assert sleep.call_count == 3 - - @mock.patch('time.sleep') - def test_retry_failure(self, sleep): - # Fail 6 times out of 5; th function is supposed to raise exc. - @retry(retries=5, interval=1, logfun=None) - def foo(): - while queue: - queue.pop() - 1 / 0 # noqa: B018 - return 1 - - queue = list(range(6)) - with pytest.raises(ZeroDivisionError): - foo() - assert sleep.call_count == 5 - - @mock.patch('time.sleep') - def test_exception_arg(self, sleep): - @retry(exception=ValueError, interval=1) - def foo(): - raise TypeError - - with pytest.raises(TypeError): - foo() - assert sleep.call_count == 0 - - @mock.patch('time.sleep') - def test_no_interval_arg(self, sleep): - # if interval is not specified sleep is not supposed to be called - - @retry(retries=5, interval=None, logfun=None) - def foo(): - 1 / 0 # noqa: B018 - - with pytest.raises(ZeroDivisionError): - foo() - assert sleep.call_count == 0 - - @mock.patch('time.sleep') - def test_retries_arg(self, sleep): - @retry(retries=5, interval=1, logfun=None) - def foo(): - 1 / 0 # noqa: B018 - - with pytest.raises(ZeroDivisionError): - foo() - assert sleep.call_count == 5 - - @mock.patch('time.sleep') - def test_retries_and_timeout_args(self, sleep): - with pytest.raises(ValueError): - retry(retries=5, timeout=1) - - -class TestSyncTestUtils(PsutilTestCase): - def test_wait_for_pid(self): - wait_for_pid(os.getpid()) - nopid = max(psutil.pids()) + 99999 - with mock.patch('psutil.tests.retry.__iter__', return_value=iter([0])): - with pytest.raises(psutil.NoSuchProcess): - wait_for_pid(nopid) - - def test_wait_for_file(self): - testfn = self.get_testfn() - with open(testfn, 'w') as f: - f.write('foo') - wait_for_file(testfn) - assert not os.path.exists(testfn) - - def test_wait_for_file_empty(self): - testfn = self.get_testfn() - with open(testfn, 'w'): - pass - wait_for_file(testfn, empty=True) - assert not os.path.exists(testfn) - - def test_wait_for_file_no_file(self): - testfn = self.get_testfn() - with mock.patch('psutil.tests.retry.__iter__', return_value=iter([0])): - with pytest.raises(OSError): - wait_for_file(testfn) - - def test_wait_for_file_no_delete(self): - testfn = self.get_testfn() - with open(testfn, 'w') as f: - f.write('foo') - wait_for_file(testfn, delete=False) - assert os.path.exists(testfn) - - def test_call_until(self): - call_until(lambda: 1) - # TODO: test for timeout - - -class TestFSTestUtils(PsutilTestCase): - def test_open_text(self): - with open_text(__file__) as f: - assert f.mode == 'r' - - def test_open_binary(self): - with open_binary(__file__) as f: - assert f.mode == 'rb' - - def test_safe_mkdir(self): - testfn = self.get_testfn() - safe_mkdir(testfn) - assert os.path.isdir(testfn) - safe_mkdir(testfn) - assert os.path.isdir(testfn) - - def test_safe_rmpath(self): - # test file is removed - testfn = self.get_testfn() - open(testfn, 'w').close() - safe_rmpath(testfn) - assert not os.path.exists(testfn) - # test no exception if path does not exist - safe_rmpath(testfn) - # test dir is removed - os.mkdir(testfn) - safe_rmpath(testfn) - assert not os.path.exists(testfn) - # test other exceptions are raised - with mock.patch( - 'psutil.tests.os.stat', side_effect=OSError(errno.EINVAL, "") - ) as m: - with pytest.raises(OSError): - safe_rmpath(testfn) - assert m.called - - def test_chdir(self): - testfn = self.get_testfn() - base = os.getcwd() - os.mkdir(testfn) - with chdir(testfn): - assert os.getcwd() == os.path.join(base, testfn) - assert os.getcwd() == base - - -class TestProcessUtils(PsutilTestCase): - def test_reap_children(self): - subp = self.spawn_testproc() - p = psutil.Process(subp.pid) - assert p.is_running() - reap_children() - assert not p.is_running() - assert not psutil.tests._pids_started - assert not psutil.tests._subprocesses_started - - def test_spawn_children_pair(self): - child, grandchild = self.spawn_children_pair() - assert child.pid != grandchild.pid - assert child.is_running() - assert grandchild.is_running() - children = psutil.Process().children() - assert children == [child] - children = psutil.Process().children(recursive=True) - assert len(children) == 2 - assert child in children - assert grandchild in children - assert child.ppid() == os.getpid() - assert grandchild.ppid() == child.pid - - terminate(child) - assert not child.is_running() - assert grandchild.is_running() - - terminate(grandchild) - assert not grandchild.is_running() - - @pytest.mark.skipif(not POSIX, reason="POSIX only") - def test_spawn_zombie(self): - _parent, zombie = self.spawn_zombie() - assert zombie.status() == psutil.STATUS_ZOMBIE - - def test_terminate(self): - # by subprocess.Popen - p = self.spawn_testproc() - terminate(p) - self.assertPidGone(p.pid) - terminate(p) - # by psutil.Process - p = psutil.Process(self.spawn_testproc().pid) - terminate(p) - self.assertPidGone(p.pid) - terminate(p) - # by psutil.Popen - cmd = [ - PYTHON_EXE, - "-c", - "import time; [time.sleep(0.1) for x in range(100)];", - ] - p = psutil.Popen( - cmd, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - env=PYTHON_EXE_ENV, - ) - terminate(p) - self.assertPidGone(p.pid) - terminate(p) - # by PID - pid = self.spawn_testproc().pid - terminate(pid) - self.assertPidGone(p.pid) - terminate(pid) - # zombie - if POSIX: - parent, zombie = self.spawn_zombie() - terminate(parent) - terminate(zombie) - self.assertPidGone(parent.pid) - self.assertPidGone(zombie.pid) - - -class TestNetUtils(PsutilTestCase): - def bind_socket(self): - port = get_free_port() - with bind_socket(addr=('', port)) as s: - assert s.getsockname()[1] == port - - @pytest.mark.skipif(not POSIX, reason="POSIX only") - def test_bind_unix_socket(self): - name = self.get_testfn() - with bind_unix_socket(name) as sock: - assert sock.family == socket.AF_UNIX - assert sock.type == socket.SOCK_STREAM - assert sock.getsockname() == name - assert os.path.exists(name) - assert stat.S_ISSOCK(os.stat(name).st_mode) - # UDP - name = self.get_testfn() - with bind_unix_socket(name, type=socket.SOCK_DGRAM) as sock: - assert sock.type == socket.SOCK_DGRAM - - def test_tcp_socketpair(self): - addr = ("127.0.0.1", get_free_port()) - server, client = tcp_socketpair(socket.AF_INET, addr=addr) - with server, client: - # Ensure they are connected and the positions are correct. - assert server.getsockname() == addr - assert client.getpeername() == addr - assert client.getsockname() != addr - - @pytest.mark.skipif(not POSIX, reason="POSIX only") - @pytest.mark.skipif( - NETBSD or FREEBSD, reason="/var/run/log UNIX socket opened by default" - ) - def test_unix_socketpair(self): - p = psutil.Process() - num_fds = p.num_fds() - assert not filter_proc_net_connections(p.net_connections(kind='unix')) - name = self.get_testfn() - server, client = unix_socketpair(name) - try: - assert os.path.exists(name) - assert stat.S_ISSOCK(os.stat(name).st_mode) - assert p.num_fds() - num_fds == 2 - assert ( - len( - filter_proc_net_connections(p.net_connections(kind='unix')) - ) - == 2 - ) - assert server.getsockname() == name - assert client.getpeername() == name - finally: - client.close() - server.close() - - def test_create_sockets(self): - with create_sockets() as socks: - fams = collections.defaultdict(int) - types = collections.defaultdict(int) - for s in socks: - fams[s.family] += 1 - # work around http://bugs.python.org/issue30204 - types[s.getsockopt(socket.SOL_SOCKET, socket.SO_TYPE)] += 1 - assert fams[socket.AF_INET] >= 2 - if supports_ipv6(): - assert fams[socket.AF_INET6] >= 2 - if POSIX and HAS_NET_CONNECTIONS_UNIX: - assert fams[socket.AF_UNIX] >= 2 - assert types[socket.SOCK_STREAM] >= 2 - assert types[socket.SOCK_DGRAM] >= 2 - - -@pytest.mark.xdist_group(name="serial") -class TestMemLeakClass(TestMemoryLeak): - @retry_on_failure() - def test_times(self): - def fun(): - cnt['cnt'] += 1 - - cnt = {'cnt': 0} - self.execute(fun, times=10, warmup_times=15) - assert cnt['cnt'] == 26 - - def test_param_err(self): - with pytest.raises(ValueError): - self.execute(lambda: 0, times=0) - with pytest.raises(ValueError): - self.execute(lambda: 0, times=-1) - with pytest.raises(ValueError): - self.execute(lambda: 0, warmup_times=-1) - with pytest.raises(ValueError): - self.execute(lambda: 0, tolerance=-1) - with pytest.raises(ValueError): - self.execute(lambda: 0, retries=-1) - - @retry_on_failure() - @pytest.mark.skipif(CI_TESTING, reason="skipped on CI") - @pytest.mark.skipif(COVERAGE, reason="skipped during test coverage") - def test_leak_mem(self): - ls = [] - - def fun(ls=ls): - ls.append("x" * 248 * 1024) - - try: - # will consume around 60M in total - with pytest.raises(AssertionError, match="extra-mem"): - self.execute(fun, times=100) - finally: - del ls - - def test_unclosed_files(self): - def fun(): - f = open(__file__) # noqa: SIM115 - self.addCleanup(f.close) - box.append(f) - - box = [] - kind = "fd" if POSIX else "handle" - with pytest.raises(AssertionError, match="unclosed " + kind): - self.execute(fun) - - def test_tolerance(self): - def fun(): - ls.append("x" * 24 * 1024) - - ls = [] - times = 100 - self.execute( - fun, times=times, warmup_times=0, tolerance=200 * 1024 * 1024 - ) - assert len(ls) == times + 1 - - def test_execute_w_exc(self): - def fun_1(): - 1 / 0 # noqa: B018 - - self.execute_w_exc(ZeroDivisionError, fun_1) - with pytest.raises(ZeroDivisionError): - self.execute_w_exc(OSError, fun_1) - - def fun_2(): - pass - - with pytest.raises(AssertionError): - self.execute_w_exc(ZeroDivisionError, fun_2) - - -class TestFakePytest(PsutilTestCase): - def run_test_class(self, klass): - suite = unittest.TestSuite() - suite.addTest(klass) - runner = unittest.TextTestRunner() - result = runner.run(suite) - return result - - def test_raises(self): - with fake_pytest.raises(ZeroDivisionError) as cm: - 1 / 0 # noqa: B018 - assert isinstance(cm.value, ZeroDivisionError) - - with fake_pytest.raises(ValueError, match="foo") as cm: - raise ValueError("foo") - - try: - with fake_pytest.raises(ValueError, match="foo") as cm: - raise ValueError("bar") - except AssertionError as err: - assert str(err) == '"foo" does not match "bar"' - else: - raise self.fail("exception not raised") - - def test_mark(self): - @fake_pytest.mark.xdist_group(name="serial") - def foo(): - return 1 - - assert foo() == 1 - - @fake_pytest.mark.xdist_group(name="serial") - class Foo: - def bar(self): - return 1 - - assert Foo().bar() == 1 - - def test_skipif(self): - class TestCase(unittest.TestCase): - @fake_pytest.mark.skipif(True, reason="reason") - def foo(self): - assert 1 == 1 # noqa: PLR0133 - - result = self.run_test_class(TestCase("foo")) - assert result.wasSuccessful() - assert len(result.skipped) == 1 - assert result.skipped[0][1] == "reason" - - class TestCase(unittest.TestCase): - @fake_pytest.mark.skipif(False, reason="reason") - def foo(self): - assert 1 == 1 # noqa: PLR0133 - - result = self.run_test_class(TestCase("foo")) - assert result.wasSuccessful() - assert len(result.skipped) == 0 - - def test_skip(self): - class TestCase(unittest.TestCase): - def foo(self): - fake_pytest.skip("reason") - assert 1 == 0 # noqa: PLR0133 - - result = self.run_test_class(TestCase("foo")) - assert result.wasSuccessful() - assert len(result.skipped) == 1 - assert result.skipped[0][1] == "reason" - - def test_main(self): - tmpdir = self.get_testfn(dir=HERE) - os.mkdir(tmpdir) - with open(os.path.join(tmpdir, "__init__.py"), "w"): - pass - with open(os.path.join(tmpdir, "test_file.py"), "w") as f: - f.write(textwrap.dedent("""\ - import unittest - - class TestCase(unittest.TestCase): - def test_passed(self): - pass - """).lstrip()) - with mock.patch.object(psutil.tests, "HERE", tmpdir): - with self.assertWarnsRegex( - UserWarning, "Fake pytest module was used" - ): - suite = fake_pytest.main() - assert suite.countTestCases() == 1 - - def test_warns(self): - # success - with fake_pytest.warns(UserWarning): - warnings.warn("foo", UserWarning, stacklevel=1) - - # failure - try: - with fake_pytest.warns(UserWarning): - warnings.warn("foo", DeprecationWarning, stacklevel=1) - except AssertionError: - pass - else: - raise self.fail("exception not raised") - - # match success - with fake_pytest.warns(UserWarning, match="foo"): - warnings.warn("foo", UserWarning, stacklevel=1) - - # match failure - try: - with fake_pytest.warns(UserWarning, match="foo"): - warnings.warn("bar", UserWarning, stacklevel=1) - except AssertionError: - pass - else: - raise self.fail("exception not raised") - - -class TestTestingUtils(PsutilTestCase): - def test_process_namespace(self): - p = psutil.Process() - ns = process_namespace(p) - ns.test() - fun = next(x for x in ns.iter(ns.getters) if x[1] == 'ppid')[0] - assert fun() == p.ppid() - - def test_system_namespace(self): - ns = system_namespace() - fun = next(x for x in ns.iter(ns.getters) if x[1] == 'net_if_addrs')[0] - assert fun() == psutil.net_if_addrs() - - -class TestOtherUtils(PsutilTestCase): - def test_is_namedtuple(self): - assert is_namedtuple(collections.namedtuple('foo', 'a b c')(1, 2, 3)) - assert not is_namedtuple(tuple()) diff --git a/PortablePython/Lib/site-packages/psutil/tests/test_unicode.py b/PortablePython/Lib/site-packages/psutil/tests/test_unicode.py deleted file mode 100644 index d8a8c4b..0000000 --- a/PortablePython/Lib/site-packages/psutil/tests/test_unicode.py +++ /dev/null @@ -1,313 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -"""Notes about unicode handling in psutil -======================================. - -Starting from version 5.3.0 psutil adds unicode support, see: -https://github.com/giampaolo/psutil/issues/1040 -The notes below apply to *any* API returning a string such as -process exe(), cwd() or username(): - -* all strings are encoded by using the OS filesystem encoding - (sys.getfilesystemencoding()) which varies depending on the platform - (e.g. "UTF-8" on macOS, "mbcs" on Win) -* no API call is supposed to crash with UnicodeDecodeError -* instead, in case of badly encoded data returned by the OS, the - following error handlers are used to replace the corrupted characters in - the string: - * sys.getfilesystemencodeerrors() or "surrogatescape" on POSIX and - "replace" on Windows. - -For a detailed explanation of how psutil handles unicode see #1040. - -Tests -===== - -List of APIs returning or dealing with a string: -('not tested' means they are not tested to deal with non-ASCII strings): - -* Process.cmdline() -* Process.cwd() -* Process.environ() -* Process.exe() -* Process.memory_maps() -* Process.name() -* Process.net_connections('unix') -* Process.open_files() -* Process.username() (not tested) - -* disk_io_counters() (not tested) -* disk_partitions() (not tested) -* disk_usage(str) -* net_connections('unix') -* net_if_addrs() (not tested) -* net_if_stats() (not tested) -* net_io_counters() (not tested) -* sensors_fans() (not tested) -* sensors_temperatures() (not tested) -* users() (not tested) - -* WindowsService.binpath() (not tested) -* WindowsService.description() (not tested) -* WindowsService.display_name() (not tested) -* WindowsService.name() (not tested) -* WindowsService.status() (not tested) -* WindowsService.username() (not tested) - -In here we create a unicode path with a funky non-ASCII name and (where -possible) make psutil return it back (e.g. on name(), exe(), open_files(), -etc.) and make sure that: - -* psutil never crashes with UnicodeDecodeError -* the returned path matches -""" - -import os -import shutil -import warnings -from contextlib import closing - -import psutil -from psutil import BSD -from psutil import POSIX -from psutil import WINDOWS -from psutil.tests import ASCII_FS -from psutil.tests import CI_TESTING -from psutil.tests import HAS_ENVIRON -from psutil.tests import HAS_MEMORY_MAPS -from psutil.tests import HAS_NET_CONNECTIONS_UNIX -from psutil.tests import INVALID_UNICODE_SUFFIX -from psutil.tests import PYPY -from psutil.tests import TESTFN_PREFIX -from psutil.tests import UNICODE_SUFFIX -from psutil.tests import PsutilTestCase -from psutil.tests import bind_unix_socket -from psutil.tests import chdir -from psutil.tests import copyload_shared_lib -from psutil.tests import create_py_exe -from psutil.tests import get_testfn -from psutil.tests import pytest -from psutil.tests import safe_mkdir -from psutil.tests import safe_rmpath -from psutil.tests import skip_on_access_denied -from psutil.tests import spawn_testproc -from psutil.tests import terminate - - -def try_unicode(suffix): - """Return True if both the fs and the subprocess module can - deal with a unicode file name. - """ - sproc = None - testfn = get_testfn(suffix=suffix) - try: - safe_rmpath(testfn) - create_py_exe(testfn) - sproc = spawn_testproc(cmd=[testfn]) - shutil.copyfile(testfn, testfn + '-2') - safe_rmpath(testfn + '-2') - except (UnicodeEncodeError, OSError): - return False - else: - return True - finally: - if sproc is not None: - terminate(sproc) - safe_rmpath(testfn) - - -# =================================================================== -# FS APIs -# =================================================================== - - -class BaseUnicodeTest(PsutilTestCase): - funky_suffix = None - - @classmethod - def setUpClass(cls): - super().setUpClass() - cls.skip_tests = False - cls.funky_name = None - if cls.funky_suffix is not None: - if not try_unicode(cls.funky_suffix): - cls.skip_tests = True - else: - cls.funky_name = get_testfn(suffix=cls.funky_suffix) - create_py_exe(cls.funky_name) - - def setUp(self): - super().setUp() - if self.skip_tests: - raise pytest.skip("can't handle unicode str") - - -@pytest.mark.xdist_group(name="serial") -@pytest.mark.skipif(ASCII_FS, reason="ASCII fs") -class TestFSAPIs(BaseUnicodeTest): - """Test FS APIs with a funky, valid, UTF8 path name.""" - - funky_suffix = UNICODE_SUFFIX - - def expect_exact_path_match(self): - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - return self.funky_name in os.listdir(".") - - # --- - - def test_proc_exe(self): - cmd = [ - self.funky_name, - "-c", - "import time; [time.sleep(0.1) for x in range(100)]", - ] - subp = self.spawn_testproc(cmd) - p = psutil.Process(subp.pid) - exe = p.exe() - assert isinstance(exe, str) - if self.expect_exact_path_match(): - assert os.path.normcase(exe) == os.path.normcase(self.funky_name) - - def test_proc_name(self): - cmd = [ - self.funky_name, - "-c", - "import time; [time.sleep(0.1) for x in range(100)]", - ] - subp = self.spawn_testproc(cmd) - name = psutil.Process(subp.pid).name() - assert isinstance(name, str) - if self.expect_exact_path_match(): - assert name == os.path.basename(self.funky_name) - - def test_proc_cmdline(self): - cmd = [ - self.funky_name, - "-c", - "import time; [time.sleep(0.1) for x in range(100)]", - ] - subp = self.spawn_testproc(cmd) - p = psutil.Process(subp.pid) - cmdline = p.cmdline() - for part in cmdline: - assert isinstance(part, str) - if self.expect_exact_path_match(): - assert cmdline == cmd - - def test_proc_cwd(self): - dname = self.funky_name + "2" - self.addCleanup(safe_rmpath, dname) - safe_mkdir(dname) - with chdir(dname): - p = psutil.Process() - cwd = p.cwd() - assert isinstance(p.cwd(), str) - if self.expect_exact_path_match(): - assert cwd == dname - - @pytest.mark.skipif(PYPY and WINDOWS, reason="fails on PYPY + WINDOWS") - def test_proc_open_files(self): - p = psutil.Process() - start = set(p.open_files()) - with open(self.funky_name, 'rb'): - new = set(p.open_files()) - path = (new - start).pop().path - assert isinstance(path, str) - if BSD and not path: - # XXX - see https://github.com/giampaolo/psutil/issues/595 - raise pytest.skip("open_files on BSD is broken") - if self.expect_exact_path_match(): - assert os.path.normcase(path) == os.path.normcase(self.funky_name) - - @pytest.mark.skipif(not POSIX, reason="POSIX only") - def test_proc_net_connections(self): - name = self.get_testfn(suffix=self.funky_suffix) - sock = bind_unix_socket(name) - with closing(sock): - conn = psutil.Process().net_connections('unix')[0] - assert isinstance(conn.laddr, str) - assert conn.laddr == name - - @pytest.mark.skipif(not POSIX, reason="POSIX only") - @pytest.mark.skipif( - not HAS_NET_CONNECTIONS_UNIX, reason="can't list UNIX sockets" - ) - @skip_on_access_denied() - def test_net_connections(self): - def find_sock(cons): - for conn in cons: - if os.path.basename(conn.laddr).startswith(TESTFN_PREFIX): - return conn - raise ValueError("connection not found") - - name = self.get_testfn(suffix=self.funky_suffix) - sock = bind_unix_socket(name) - with closing(sock): - cons = psutil.net_connections(kind='unix') - conn = find_sock(cons) - assert isinstance(conn.laddr, str) - assert conn.laddr == name - - def test_disk_usage(self): - dname = self.funky_name + "2" - self.addCleanup(safe_rmpath, dname) - safe_mkdir(dname) - psutil.disk_usage(dname) - - @pytest.mark.skipif(not HAS_MEMORY_MAPS, reason="not supported") - @pytest.mark.skipif(PYPY, reason="unstable on PYPY") - def test_memory_maps(self): - with copyload_shared_lib(suffix=self.funky_suffix) as funky_path: - - def normpath(p): - return os.path.realpath(os.path.normcase(p)) - - libpaths = [ - normpath(x.path) for x in psutil.Process().memory_maps() - ] - # ...just to have a clearer msg in case of failure - libpaths = [x for x in libpaths if TESTFN_PREFIX in x] - assert normpath(funky_path) in libpaths - for path in libpaths: - assert isinstance(path, str) - - -@pytest.mark.skipif(CI_TESTING, reason="unreliable on CI") -class TestFSAPIsWithInvalidPath(TestFSAPIs): - """Test FS APIs with a funky, invalid path name.""" - - funky_suffix = INVALID_UNICODE_SUFFIX - - def expect_exact_path_match(self): - return True - - -# =================================================================== -# Non fs APIs -# =================================================================== - - -class TestNonFSAPIS(BaseUnicodeTest): - """Unicode tests for non fs-related APIs.""" - - funky_suffix = UNICODE_SUFFIX - - @pytest.mark.skipif(not HAS_ENVIRON, reason="not supported") - @pytest.mark.skipif(PYPY and WINDOWS, reason="segfaults on PYPY + WINDOWS") - def test_proc_environ(self): - # Note: differently from others, this test does not deal - # with fs paths. - env = os.environ.copy() - env['FUNNY_ARG'] = self.funky_suffix - sproc = self.spawn_testproc(env=env) - p = psutil.Process(sproc.pid) - env = p.environ() - for k, v in env.items(): - assert isinstance(k, str) - assert isinstance(v, str) - assert env['FUNNY_ARG'] == self.funky_suffix diff --git a/PortablePython/Lib/site-packages/psutil/tests/test_windows.py b/PortablePython/Lib/site-packages/psutil/tests/test_windows.py deleted file mode 100644 index c5c536b..0000000 --- a/PortablePython/Lib/site-packages/psutil/tests/test_windows.py +++ /dev/null @@ -1,914 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -"""Windows specific tests.""" - -import datetime -import glob -import os -import platform -import re -import shutil -import signal -import subprocess -import sys -import time -import warnings -from unittest import mock - -import psutil -from psutil import WINDOWS -from psutil.tests import GITHUB_ACTIONS -from psutil.tests import HAS_BATTERY -from psutil.tests import IS_64BIT -from psutil.tests import PYPY -from psutil.tests import TOLERANCE_DISK_USAGE -from psutil.tests import TOLERANCE_SYS_MEM -from psutil.tests import PsutilTestCase -from psutil.tests import pytest -from psutil.tests import retry_on_failure -from psutil.tests import sh -from psutil.tests import spawn_testproc -from psutil.tests import terminate - - -if WINDOWS and not PYPY: - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - import win32api # requires "pip install pywin32" - import win32con - import win32process - import wmi # requires "pip install wmi" / "make install-pydeps-test" - -if WINDOWS: - from psutil._pswindows import convert_oserror - - -cext = psutil._psplatform.cext - - -@pytest.mark.skipif(not WINDOWS, reason="WINDOWS only") -@pytest.mark.skipif(PYPY, reason="pywin32 not available on PYPY") -class WindowsTestCase(PsutilTestCase): - pass - - -def powershell(cmd): - """Currently not used, but available just in case. Usage: - - >>> powershell( - "Get-CIMInstance Win32_PageFileUsage | Select AllocatedBaseSize") - """ - if not shutil.which("powershell.exe"): - raise pytest.skip("powershell.exe not available") - cmdline = ( - "powershell.exe -ExecutionPolicy Bypass -NoLogo -NonInteractive " - f"-NoProfile -WindowStyle Hidden -Command \"{cmd}\"" # noqa: Q003 - ) - return sh(cmdline) - - -def wmic(path, what, converter=int): - """Currently not used, but available just in case. Usage: - - >>> wmic("Win32_OperatingSystem", "FreePhysicalMemory") - 2134124534 - """ - out = sh(f"wmic path {path} get {what}").strip() - data = "".join(out.splitlines()[1:]).strip() # get rid of the header - if converter is not None: - if "," in what: - return tuple(converter(x) for x in data.split()) - else: - return converter(data) - else: - return data - - -# =================================================================== -# System APIs -# =================================================================== - - -class TestCpuAPIs(WindowsTestCase): - @pytest.mark.skipif( - 'NUMBER_OF_PROCESSORS' not in os.environ, - reason="NUMBER_OF_PROCESSORS env var is not available", - ) - def test_cpu_count_vs_NUMBER_OF_PROCESSORS(self): - # Will likely fail on many-cores systems: - # https://stackoverflow.com/questions/31209256 - num_cpus = int(os.environ['NUMBER_OF_PROCESSORS']) - assert num_cpus == psutil.cpu_count() - - def test_cpu_count_vs_GetSystemInfo(self): - # Will likely fail on many-cores systems: - # https://stackoverflow.com/questions/31209256 - sys_value = win32api.GetSystemInfo()[5] - psutil_value = psutil.cpu_count() - assert sys_value == psutil_value - - def test_cpu_count_logical_vs_wmi(self): - w = wmi.WMI() - procs = sum( - proc.NumberOfLogicalProcessors for proc in w.Win32_Processor() - ) - assert psutil.cpu_count() == procs - - def test_cpu_count_cores_vs_wmi(self): - w = wmi.WMI() - cores = sum(proc.NumberOfCores for proc in w.Win32_Processor()) - assert psutil.cpu_count(logical=False) == cores - - def test_cpu_count_vs_cpu_times(self): - assert psutil.cpu_count() == len(psutil.cpu_times(percpu=True)) - - def test_cpu_freq(self): - w = wmi.WMI() - proc = w.Win32_Processor()[0] - assert proc.CurrentClockSpeed == psutil.cpu_freq().current - assert proc.MaxClockSpeed == psutil.cpu_freq().max - - -class TestSystemAPIs(WindowsTestCase): - def test_nic_names(self): - out = sh('ipconfig /all') - nics = psutil.net_io_counters(pernic=True).keys() - for nic in nics: - if "pseudo-interface" in nic.replace(' ', '-').lower(): - continue - if nic not in out: - raise self.fail( - f"{nic!r} nic wasn't found in 'ipconfig /all' output" - ) - - def test_total_phymem(self): - w = wmi.WMI().Win32_ComputerSystem()[0] - assert int(w.TotalPhysicalMemory) == psutil.virtual_memory().total - - def test_free_phymem(self): - w = wmi.WMI().Win32_PerfRawData_PerfOS_Memory()[0] - assert ( - abs(int(w.AvailableBytes) - psutil.virtual_memory().free) - < TOLERANCE_SYS_MEM - ) - - def test_total_swapmem(self): - w = wmi.WMI().Win32_PerfRawData_PerfOS_Memory()[0] - assert ( - int(w.CommitLimit) - psutil.virtual_memory().total - == psutil.swap_memory().total - ) - if psutil.swap_memory().total == 0: - assert psutil.swap_memory().free == 0 - assert psutil.swap_memory().used == 0 - - def test_percent_swapmem(self): - if psutil.swap_memory().total > 0: - w = wmi.WMI().Win32_PerfRawData_PerfOS_PagingFile(Name="_Total")[0] - # calculate swap usage to percent - percentSwap = int(w.PercentUsage) * 100 / int(w.PercentUsage_Base) - # exact percent may change but should be reasonable - # assert within +/- 5% and between 0 and 100% - assert psutil.swap_memory().percent >= 0 - assert abs(psutil.swap_memory().percent - percentSwap) < 5 - assert psutil.swap_memory().percent <= 100 - - # @pytest.mark.skipif(wmi is None, reason="wmi module is not installed") - # def test__UPTIME(self): - # # _UPTIME constant is not public but it is used internally - # # as value to return for pid 0 creation time. - # # WMI behaves the same. - # w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] - # p = psutil.Process(0) - # wmic_create = str(w.CreationDate.split('.')[0]) - # psutil_create = time.strftime("%Y%m%d%H%M%S", - # time.localtime(p.create_time())) - - # Note: this test is not very reliable - @retry_on_failure() - def test_pids(self): - # Note: this test might fail if the OS is starting/killing - # other processes in the meantime - w = wmi.WMI().Win32_Process() - wmi_pids = {x.ProcessId for x in w} - psutil_pids = set(psutil.pids()) - assert wmi_pids == psutil_pids - - @retry_on_failure() - def test_disks(self): - ps_parts = psutil.disk_partitions(all=True) - wmi_parts = wmi.WMI().Win32_LogicalDisk() - for ps_part in ps_parts: - for wmi_part in wmi_parts: - if ps_part.device.replace('\\', '') == wmi_part.DeviceID: - if not ps_part.mountpoint: - # this is usually a CD-ROM with no disk inserted - break - if 'cdrom' in ps_part.opts: - break - if ps_part.mountpoint.startswith('A:'): - break # floppy - try: - usage = psutil.disk_usage(ps_part.mountpoint) - except FileNotFoundError: - # usually this is the floppy - break - assert usage.total == int(wmi_part.Size) - wmi_free = int(wmi_part.FreeSpace) - assert usage.free == wmi_free - # 10 MB tolerance - if abs(usage.free - wmi_free) > 10 * 1024 * 1024: - raise self.fail(f"psutil={usage.free}, wmi={wmi_free}") - break - else: - raise self.fail(f"can't find partition {ps_part!r}") - - @retry_on_failure() - def test_disk_usage(self): - for disk in psutil.disk_partitions(): - if 'cdrom' in disk.opts: - continue - sys_value = win32api.GetDiskFreeSpaceEx(disk.mountpoint) - psutil_value = psutil.disk_usage(disk.mountpoint) - assert abs(sys_value[0] - psutil_value.free) < TOLERANCE_DISK_USAGE - assert ( - abs(sys_value[1] - psutil_value.total) < TOLERANCE_DISK_USAGE - ) - assert psutil_value.used == psutil_value.total - psutil_value.free - - def test_disk_partitions(self): - sys_value = [ - x + '\\' - for x in win32api.GetLogicalDriveStrings().split("\\\x00") - if x and not x.startswith('A:') - ] - psutil_value = [ - x.mountpoint - for x in psutil.disk_partitions(all=True) - if not x.mountpoint.startswith('A:') - ] - assert sys_value == psutil_value - - def test_net_if_stats(self): - ps_names = set(cext.net_if_stats()) - wmi_adapters = wmi.WMI().Win32_NetworkAdapter() - wmi_names = set() - for wmi_adapter in wmi_adapters: - wmi_names.add(wmi_adapter.Name) - wmi_names.add(wmi_adapter.NetConnectionID) - assert ( - ps_names & wmi_names - ), f"no common entries in {ps_names}, {wmi_names}" - - def test_boot_time(self): - wmi_os = wmi.WMI().Win32_OperatingSystem() - wmi_btime_str = wmi_os[0].LastBootUpTime.split('.')[0] - wmi_btime_dt = datetime.datetime.strptime( - wmi_btime_str, "%Y%m%d%H%M%S" - ) - psutil_dt = datetime.datetime.fromtimestamp(psutil.boot_time()) - diff = abs((wmi_btime_dt - psutil_dt).total_seconds()) - assert diff <= 5 - - def test_boot_time_fluctuation(self): - # https://github.com/giampaolo/psutil/issues/1007 - with mock.patch('psutil._pswindows.cext.boot_time', return_value=5): - assert psutil.boot_time() == 5 - with mock.patch('psutil._pswindows.cext.boot_time', return_value=4): - assert psutil.boot_time() == 5 - with mock.patch('psutil._pswindows.cext.boot_time', return_value=6): - assert psutil.boot_time() == 5 - with mock.patch('psutil._pswindows.cext.boot_time', return_value=333): - assert psutil.boot_time() == 333 - - -# =================================================================== -# sensors_battery() -# =================================================================== - - -class TestSensorsBattery(WindowsTestCase): - def test_has_battery(self): - if win32api.GetPwrCapabilities()['SystemBatteriesPresent']: - assert psutil.sensors_battery() is not None - else: - assert psutil.sensors_battery() is None - - @pytest.mark.skipif(not HAS_BATTERY, reason="no battery") - def test_percent(self): - w = wmi.WMI() - battery_wmi = w.query('select * from Win32_Battery')[0] - battery_psutil = psutil.sensors_battery() - assert ( - abs(battery_psutil.percent - battery_wmi.EstimatedChargeRemaining) - < 1 - ) - - @pytest.mark.skipif(not HAS_BATTERY, reason="no battery") - def test_power_plugged(self): - w = wmi.WMI() - battery_wmi = w.query('select * from Win32_Battery')[0] - battery_psutil = psutil.sensors_battery() - # Status codes: - # https://msdn.microsoft.com/en-us/library/aa394074(v=vs.85).aspx - assert battery_psutil.power_plugged == (battery_wmi.BatteryStatus == 2) - - def test_emulate_no_battery(self): - with mock.patch( - "psutil._pswindows.cext.sensors_battery", - return_value=(0, 128, 0, 0), - ) as m: - assert psutil.sensors_battery() is None - assert m.called - - def test_emulate_power_connected(self): - with mock.patch( - "psutil._pswindows.cext.sensors_battery", return_value=(1, 0, 0, 0) - ) as m: - assert ( - psutil.sensors_battery().secsleft - == psutil.POWER_TIME_UNLIMITED - ) - assert m.called - - def test_emulate_power_charging(self): - with mock.patch( - "psutil._pswindows.cext.sensors_battery", return_value=(0, 8, 0, 0) - ) as m: - assert ( - psutil.sensors_battery().secsleft - == psutil.POWER_TIME_UNLIMITED - ) - assert m.called - - def test_emulate_secs_left_unknown(self): - with mock.patch( - "psutil._pswindows.cext.sensors_battery", - return_value=(0, 0, 0, -1), - ) as m: - assert ( - psutil.sensors_battery().secsleft == psutil.POWER_TIME_UNKNOWN - ) - assert m.called - - -# =================================================================== -# Process APIs -# =================================================================== - - -class TestProcess(WindowsTestCase): - @classmethod - def setUpClass(cls): - cls.pid = spawn_testproc().pid - - @classmethod - def tearDownClass(cls): - terminate(cls.pid) - - def test_issue_24(self): - p = psutil.Process(0) - with pytest.raises(psutil.AccessDenied): - p.kill() - - def test_special_pid(self): - p = psutil.Process(4) - assert p.name() == 'System' - # use __str__ to access all common Process properties to check - # that nothing strange happens - str(p) - p.username() - assert p.create_time() >= 0.0 - try: - rss, _vms = p.memory_info()[:2] - except psutil.AccessDenied: - # expected on Windows Vista and Windows 7 - if platform.uname()[1] not in {'vista', 'win-7', 'win7'}: - raise - else: - assert rss > 0 - - def test_send_signal(self): - p = psutil.Process(self.pid) - with pytest.raises(ValueError): - p.send_signal(signal.SIGINT) - - def test_num_handles_increment(self): - p = psutil.Process(os.getpid()) - before = p.num_handles() - handle = win32api.OpenProcess( - win32con.PROCESS_QUERY_INFORMATION, win32con.FALSE, os.getpid() - ) - after = p.num_handles() - assert after == before + 1 - win32api.CloseHandle(handle) - assert p.num_handles() == before - - def test_ctrl_signals(self): - p = psutil.Process(self.spawn_testproc().pid) - p.send_signal(signal.CTRL_C_EVENT) - p.send_signal(signal.CTRL_BREAK_EVENT) - p.kill() - p.wait() - with pytest.raises(psutil.NoSuchProcess): - p.send_signal(signal.CTRL_C_EVENT) - with pytest.raises(psutil.NoSuchProcess): - p.send_signal(signal.CTRL_BREAK_EVENT) - - def test_username(self): - name = win32api.GetUserNameEx(win32con.NameSamCompatible) - if name.endswith('$'): - # When running as a service account (most likely to be - # NetworkService), these user name calculations don't produce the - # same result, causing the test to fail. - raise pytest.skip('running as service account') - assert psutil.Process().username() == name - - def test_cmdline(self): - sys_value = re.sub(r"[ ]+", " ", win32api.GetCommandLine()).strip() - psutil_value = ' '.join(psutil.Process().cmdline()) - if sys_value[0] == '"' != psutil_value[0]: - # The PyWin32 command line may retain quotes around argv[0] if they - # were used unnecessarily, while psutil will omit them. So remove - # the first 2 quotes from sys_value if not in psutil_value. - # A path to an executable will not contain quotes, so this is safe. - sys_value = sys_value.replace('"', '', 2) - assert sys_value == psutil_value - - # XXX - occasional failures - - # def test_cpu_times(self): - # handle = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION, - # win32con.FALSE, os.getpid()) - # self.addCleanup(win32api.CloseHandle, handle) - # sys_value = win32process.GetProcessTimes(handle) - # psutil_value = psutil.Process().cpu_times() - # self.assertAlmostEqual( - # psutil_value.user, sys_value['UserTime'] / 10000000.0, - # delta=0.2) - # self.assertAlmostEqual( - # psutil_value.user, sys_value['KernelTime'] / 10000000.0, - # delta=0.2) - - def test_nice(self): - handle = win32api.OpenProcess( - win32con.PROCESS_QUERY_INFORMATION, win32con.FALSE, os.getpid() - ) - self.addCleanup(win32api.CloseHandle, handle) - sys_value = win32process.GetPriorityClass(handle) - psutil_value = psutil.Process().nice() - assert psutil_value == sys_value - - def test_memory_info(self): - handle = win32api.OpenProcess( - win32con.PROCESS_QUERY_INFORMATION, win32con.FALSE, self.pid - ) - self.addCleanup(win32api.CloseHandle, handle) - sys_value = win32process.GetProcessMemoryInfo(handle) - psutil_value = psutil.Process(self.pid).memory_info() - assert sys_value['PeakWorkingSetSize'] == psutil_value.peak_wset - assert sys_value['WorkingSetSize'] == psutil_value.wset - assert ( - sys_value['QuotaPeakPagedPoolUsage'] - == psutil_value.peak_paged_pool - ) - assert sys_value['QuotaPagedPoolUsage'] == psutil_value.paged_pool - assert ( - sys_value['QuotaPeakNonPagedPoolUsage'] - == psutil_value.peak_nonpaged_pool - ) - assert ( - sys_value['QuotaNonPagedPoolUsage'] == psutil_value.nonpaged_pool - ) - assert sys_value['PagefileUsage'] == psutil_value.pagefile - assert sys_value['PeakPagefileUsage'] == psutil_value.peak_pagefile - - assert psutil_value.rss == psutil_value.wset - assert psutil_value.vms == psutil_value.pagefile - - def test_wait(self): - handle = win32api.OpenProcess( - win32con.PROCESS_QUERY_INFORMATION, win32con.FALSE, self.pid - ) - self.addCleanup(win32api.CloseHandle, handle) - p = psutil.Process(self.pid) - p.terminate() - psutil_value = p.wait() - sys_value = win32process.GetExitCodeProcess(handle) - assert psutil_value == sys_value - - def test_cpu_affinity(self): - def from_bitmask(x): - return [i for i in range(64) if (1 << i) & x] - - handle = win32api.OpenProcess( - win32con.PROCESS_QUERY_INFORMATION, win32con.FALSE, self.pid - ) - self.addCleanup(win32api.CloseHandle, handle) - sys_value = from_bitmask( - win32process.GetProcessAffinityMask(handle)[0] - ) - psutil_value = psutil.Process(self.pid).cpu_affinity() - assert psutil_value == sys_value - - def test_io_counters(self): - handle = win32api.OpenProcess( - win32con.PROCESS_QUERY_INFORMATION, win32con.FALSE, os.getpid() - ) - self.addCleanup(win32api.CloseHandle, handle) - sys_value = win32process.GetProcessIoCounters(handle) - psutil_value = psutil.Process().io_counters() - assert psutil_value.read_count == sys_value['ReadOperationCount'] - assert psutil_value.write_count == sys_value['WriteOperationCount'] - assert psutil_value.read_bytes == sys_value['ReadTransferCount'] - assert psutil_value.write_bytes == sys_value['WriteTransferCount'] - assert psutil_value.other_count == sys_value['OtherOperationCount'] - assert psutil_value.other_bytes == sys_value['OtherTransferCount'] - - def test_num_handles(self): - import ctypes - import ctypes.wintypes - - PROCESS_QUERY_INFORMATION = 0x400 - handle = ctypes.windll.kernel32.OpenProcess( - PROCESS_QUERY_INFORMATION, 0, self.pid - ) - self.addCleanup(ctypes.windll.kernel32.CloseHandle, handle) - - hndcnt = ctypes.wintypes.DWORD() - ctypes.windll.kernel32.GetProcessHandleCount( - handle, ctypes.byref(hndcnt) - ) - sys_value = hndcnt.value - psutil_value = psutil.Process(self.pid).num_handles() - assert psutil_value == sys_value - - def test_error_partial_copy(self): - # https://github.com/giampaolo/psutil/issues/875 - exc = OSError() - exc.winerror = 299 - with mock.patch("psutil._psplatform.cext.proc_cwd", side_effect=exc): - with mock.patch("time.sleep") as m: - p = psutil.Process() - with pytest.raises(psutil.AccessDenied): - p.cwd() - assert m.call_count >= 5 - - def test_exe(self): - # NtQuerySystemInformation succeeds if process is gone. Make sure - # it raises NSP for a non existent pid. - pid = psutil.pids()[-1] + 99999 - proc = psutil._psplatform.Process(pid) - with pytest.raises(psutil.NoSuchProcess): - proc.exe() - - -class TestProcessWMI(WindowsTestCase): - """Compare Process API results with WMI.""" - - @classmethod - def setUpClass(cls): - cls.pid = spawn_testproc().pid - - @classmethod - def tearDownClass(cls): - terminate(cls.pid) - - def test_name(self): - w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] - p = psutil.Process(self.pid) - assert p.name() == w.Caption - - # This fail on github because using virtualenv for test environment - @pytest.mark.skipif( - GITHUB_ACTIONS, reason="unreliable path on GITHUB_ACTIONS" - ) - def test_exe(self): - w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] - p = psutil.Process(self.pid) - # Note: wmi reports the exe as a lower case string. - # Being Windows paths case-insensitive we ignore that. - assert p.exe().lower() == w.ExecutablePath.lower() - - def test_cmdline(self): - w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] - p = psutil.Process(self.pid) - assert ' '.join(p.cmdline()) == w.CommandLine.replace('"', '') - - def test_username(self): - w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] - p = psutil.Process(self.pid) - domain, _, username = w.GetOwner() - username = f"{domain}\\{username}" - assert p.username() == username - - @retry_on_failure() - def test_memory_rss(self): - w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] - p = psutil.Process(self.pid) - rss = p.memory_info().rss - assert rss == int(w.WorkingSetSize) - - @retry_on_failure() - def test_memory_vms(self): - w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] - p = psutil.Process(self.pid) - vms = p.memory_info().vms - # http://msdn.microsoft.com/en-us/library/aa394372(VS.85).aspx - # ...claims that PageFileUsage is represented in Kilo - # bytes but funnily enough on certain platforms bytes are - # returned instead. - wmi_usage = int(w.PageFileUsage) - if vms not in {wmi_usage, wmi_usage * 1024}: - raise self.fail(f"wmi={wmi_usage}, psutil={vms}") - - def test_create_time(self): - w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] - p = psutil.Process(self.pid) - wmic_create = str(w.CreationDate.split('.')[0]) - psutil_create = time.strftime( - "%Y%m%d%H%M%S", time.localtime(p.create_time()) - ) - assert wmic_create == psutil_create - - -# --- - - -@pytest.mark.skipif(not WINDOWS, reason="WINDOWS only") -class TestDualProcessImplementation(PsutilTestCase): - """Certain APIs on Windows have 2 internal implementations, one - based on documented Windows APIs, another one based - NtQuerySystemInformation() which gets called as fallback in - case the first fails because of limited permission error. - Here we test that the two methods return the exact same value, - see: - https://github.com/giampaolo/psutil/issues/304. - """ - - @classmethod - def setUpClass(cls): - cls.pid = spawn_testproc().pid - - @classmethod - def tearDownClass(cls): - terminate(cls.pid) - - def test_memory_info(self): - mem_1 = psutil.Process(self.pid).memory_info() - with mock.patch( - "psutil._psplatform.cext.proc_memory_info", - side_effect=PermissionError, - ) as fun: - mem_2 = psutil.Process(self.pid).memory_info() - assert len(mem_1) == len(mem_2) - for i in range(len(mem_1)): - assert mem_1[i] >= 0 - assert mem_2[i] >= 0 - assert abs(mem_1[i] - mem_2[i]) < 512 - assert fun.called - - def test_create_time(self): - ctime = psutil.Process(self.pid).create_time() - with mock.patch( - "psutil._psplatform.cext.proc_times", - side_effect=PermissionError, - ) as fun: - assert psutil.Process(self.pid).create_time() == ctime - assert fun.called - - def test_cpu_times(self): - cpu_times_1 = psutil.Process(self.pid).cpu_times() - with mock.patch( - "psutil._psplatform.cext.proc_times", - side_effect=PermissionError, - ) as fun: - cpu_times_2 = psutil.Process(self.pid).cpu_times() - assert fun.called - assert abs(cpu_times_1.user - cpu_times_2.user) < 0.01 - assert abs(cpu_times_1.system - cpu_times_2.system) < 0.01 - - def test_io_counters(self): - io_counters_1 = psutil.Process(self.pid).io_counters() - with mock.patch( - "psutil._psplatform.cext.proc_io_counters", - side_effect=PermissionError, - ) as fun: - io_counters_2 = psutil.Process(self.pid).io_counters() - for i in range(len(io_counters_1)): - assert abs(io_counters_1[i] - io_counters_2[i]) < 5 - assert fun.called - - def test_num_handles(self): - num_handles = psutil.Process(self.pid).num_handles() - with mock.patch( - "psutil._psplatform.cext.proc_num_handles", - side_effect=PermissionError, - ) as fun: - assert psutil.Process(self.pid).num_handles() == num_handles - assert fun.called - - def test_cmdline(self): - for pid in psutil.pids(): - try: - a = cext.proc_cmdline(pid, use_peb=True) - b = cext.proc_cmdline(pid, use_peb=False) - except OSError as err: - err = convert_oserror(err) - if not isinstance( - err, (psutil.AccessDenied, psutil.NoSuchProcess) - ): - raise - else: - assert a == b - - -@pytest.mark.skipif(not WINDOWS, reason="WINDOWS only") -class RemoteProcessTestCase(PsutilTestCase): - """Certain functions require calling ReadProcessMemory. - This trivially works when called on the current process. - Check that this works on other processes, especially when they - have a different bitness. - """ - - @staticmethod - def find_other_interpreter(): - # find a python interpreter that is of the opposite bitness from us - code = "import sys; sys.stdout.write(str(sys.maxsize > 2**32))" - - # XXX: a different and probably more stable approach might be to access - # the registry but accessing 64 bit paths from a 32 bit process - for filename in glob.glob(r"C:\Python*\python.exe"): - proc = subprocess.Popen( - args=[filename, "-c", code], - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - ) - output, _ = proc.communicate() - proc.wait() - if output == str(not IS_64BIT): - return filename - - test_args = ["-c", "import sys; sys.stdin.read()"] - - def setUp(self): - super().setUp() - - other_python = self.find_other_interpreter() - if other_python is None: - raise pytest.skip( - "could not find interpreter with opposite bitness" - ) - if IS_64BIT: - self.python64 = sys.executable - self.python32 = other_python - else: - self.python64 = other_python - self.python32 = sys.executable - - env = os.environ.copy() - env["THINK_OF_A_NUMBER"] = str(os.getpid()) - self.proc32 = self.spawn_testproc( - [self.python32] + self.test_args, env=env, stdin=subprocess.PIPE - ) - self.proc64 = self.spawn_testproc( - [self.python64] + self.test_args, env=env, stdin=subprocess.PIPE - ) - - def tearDown(self): - super().tearDown() - self.proc32.communicate() - self.proc64.communicate() - - def test_cmdline_32(self): - p = psutil.Process(self.proc32.pid) - assert len(p.cmdline()) == 3 - assert p.cmdline()[1:] == self.test_args - - def test_cmdline_64(self): - p = psutil.Process(self.proc64.pid) - assert len(p.cmdline()) == 3 - assert p.cmdline()[1:] == self.test_args - - def test_cwd_32(self): - p = psutil.Process(self.proc32.pid) - assert p.cwd() == os.getcwd() - - def test_cwd_64(self): - p = psutil.Process(self.proc64.pid) - assert p.cwd() == os.getcwd() - - def test_environ_32(self): - p = psutil.Process(self.proc32.pid) - e = p.environ() - assert "THINK_OF_A_NUMBER" in e - assert e["THINK_OF_A_NUMBER"] == str(os.getpid()) - - def test_environ_64(self): - p = psutil.Process(self.proc64.pid) - try: - p.environ() - except psutil.AccessDenied: - pass - - -# =================================================================== -# Windows services -# =================================================================== - - -@pytest.mark.skipif(not WINDOWS, reason="WINDOWS only") -class TestServices(PsutilTestCase): - def test_win_service_iter(self): - valid_statuses = { - "running", - "paused", - "start", - "pause", - "continue", - "stop", - "stopped", - } - valid_start_types = {"automatic", "manual", "disabled"} - valid_statuses = { - "running", - "paused", - "start_pending", - "pause_pending", - "continue_pending", - "stop_pending", - "stopped", - } - for serv in psutil.win_service_iter(): - data = serv.as_dict() - assert isinstance(data['name'], str) - assert data['name'].strip() - assert isinstance(data['display_name'], str) - assert isinstance(data['username'], str) - assert data['status'] in valid_statuses - if data['pid'] is not None: - psutil.Process(data['pid']) - assert isinstance(data['binpath'], str) - assert isinstance(data['username'], str) - assert isinstance(data['start_type'], str) - assert data['start_type'] in valid_start_types - assert data['status'] in valid_statuses - assert isinstance(data['description'], str) - pid = serv.pid() - if pid is not None: - p = psutil.Process(pid) - assert p.is_running() - # win_service_get - s = psutil.win_service_get(serv.name()) - # test __eq__ - assert serv == s - - def test_win_service_get(self): - ERROR_SERVICE_DOES_NOT_EXIST = ( - psutil._psplatform.cext.ERROR_SERVICE_DOES_NOT_EXIST - ) - ERROR_ACCESS_DENIED = psutil._psplatform.cext.ERROR_ACCESS_DENIED - - name = next(psutil.win_service_iter()).name() - with pytest.raises(psutil.NoSuchProcess) as cm: - psutil.win_service_get(name + '???') - assert cm.value.name == name + '???' - - # test NoSuchProcess - service = psutil.win_service_get(name) - exc = OSError(0, "msg", 0) - exc.winerror = ERROR_SERVICE_DOES_NOT_EXIST - with mock.patch( - "psutil._psplatform.cext.winservice_query_status", side_effect=exc - ): - with pytest.raises(psutil.NoSuchProcess): - service.status() - with mock.patch( - "psutil._psplatform.cext.winservice_query_config", side_effect=exc - ): - with pytest.raises(psutil.NoSuchProcess): - service.username() - - # test AccessDenied - exc = OSError(0, "msg", 0) - exc.winerror = ERROR_ACCESS_DENIED - with mock.patch( - "psutil._psplatform.cext.winservice_query_status", side_effect=exc - ): - with pytest.raises(psutil.AccessDenied): - service.status() - with mock.patch( - "psutil._psplatform.cext.winservice_query_config", side_effect=exc - ): - with pytest.raises(psutil.AccessDenied): - service.username() - - # test __str__ and __repr__ - assert service.name() in str(service) - assert service.display_name() in str(service) - assert service.name() in repr(service) - assert service.display_name() in repr(service) diff --git a/PortablePython/Lib/site-packages/pynput-1.8.1.dist-info/COPYING.LGPL b/PortablePython/Lib/site-packages/pynput-1.8.1.dist-info/COPYING.LGPL deleted file mode 100644 index 0df067b..0000000 --- a/PortablePython/Lib/site-packages/pynput-1.8.1.dist-info/COPYING.LGPL +++ /dev/null @@ -1,165 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007-2024 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - - This version of the GNU Lesser General Public License incorporates -the terms and conditions of version 3 of the GNU General Public -License, supplemented by the additional permissions listed below. - - 0. Additional Definitions. - - As used herein, "this License" refers to version 3 of the GNU Lesser -General Public License, and the "GNU GPL" refers to version 3 of the GNU -General Public License. - - "The Library" refers to a covered work governed by this License, -other than an Application or a Combined Work as defined below. - - An "Application" is any work that makes use of an interface provided -by the Library, but which is not otherwise based on the Library. -Defining a subclass of a class defined by the Library is deemed a mode -of using an interface provided by the Library. - - A "Combined Work" is a work produced by combining or linking an -Application with the Library. The particular version of the Library -with which the Combined Work was made is also called the "Linked -Version". - - The "Minimal Corresponding Source" for a Combined Work means the -Corresponding Source for the Combined Work, excluding any source code -for portions of the Combined Work that, considered in isolation, are -based on the Application, and not on the Linked Version. - - The "Corresponding Application Code" for a Combined Work means the -object code and/or source code for the Application, including any data -and utility programs needed for reproducing the Combined Work from the -Application, but excluding the System Libraries of the Combined Work. - - 1. Exception to Section 3 of the GNU GPL. - - You may convey a covered work under sections 3 and 4 of this License -without being bound by section 3 of the GNU GPL. - - 2. Conveying Modified Versions. - - If you modify a copy of the Library, and, in your modifications, a -facility refers to a function or data to be supplied by an Application -that uses the facility (other than as an argument passed when the -facility is invoked), then you may convey a copy of the modified -version: - - a) under this License, provided that you make a good faith effort to - ensure that, in the event an Application does not supply the - function or data, the facility still operates, and performs - whatever part of its purpose remains meaningful, or - - b) under the GNU GPL, with none of the additional permissions of - this License applicable to that copy. - - 3. Object Code Incorporating Material from Library Header Files. - - The object code form of an Application may incorporate material from -a header file that is part of the Library. You may convey such object -code under terms of your choice, provided that, if the incorporated -material is not limited to numerical parameters, data structure -layouts and accessors, or small macros, inline functions and templates -(ten or fewer lines in length), you do both of the following: - - a) Give prominent notice with each copy of the object code that the - Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the object code with a copy of the GNU GPL and this license - document. - - 4. Combined Works. - - You may convey a Combined Work under terms of your choice that, -taken together, effectively do not restrict modification of the -portions of the Library contained in the Combined Work and reverse -engineering for debugging such modifications, if you also do each of -the following: - - a) Give prominent notice with each copy of the Combined Work that - the Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the Combined Work with a copy of the GNU GPL and this license - document. - - c) For a Combined Work that displays copyright notices during - execution, include the copyright notice for the Library among - these notices, as well as a reference directing the user to the - copies of the GNU GPL and this license document. - - d) Do one of the following: - - 0) Convey the Minimal Corresponding Source under the terms of this - License, and the Corresponding Application Code in a form - suitable for, and under terms that permit, the user to - recombine or relink the Application with a modified version of - the Linked Version to produce a modified Combined Work, in the - manner specified by section 6 of the GNU GPL for conveying - Corresponding Source. - - 1) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (a) uses at run time - a copy of the Library already present on the user's computer - system, and (b) will operate properly with a modified version - of the Library that is interface-compatible with the Linked - Version. - - e) Provide Installation Information, but only if you would otherwise - be required to provide such information under section 6 of the - GNU GPL, and only to the extent that such information is - necessary to install and execute a modified version of the - Combined Work produced by recombining or relinking the - Application with a modified version of the Linked Version. (If - you use option 4d0, the Installation Information must accompany - the Minimal Corresponding Source and Corresponding Application - Code. If you use option 4d1, you must provide the Installation - Information in the manner specified by section 6 of the GNU GPL - for conveying Corresponding Source.) - - 5. Combined Libraries. - - 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 that are not Applications and are not covered by this -License, and convey such a combined library under terms of your -choice, if you do both of the following: - - a) Accompany the combined library with a copy of the same work based - on the Library, uncombined with any other library facilities, - conveyed under the terms of this License. - - b) Give prominent notice with the combined library that part of it - is a work based on the Library, and explaining where to find the - accompanying uncombined form of the same work. - - 6. Revised Versions of the GNU Lesser General Public License. - - The Free Software Foundation may publish revised and/or new versions -of the GNU 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 as you received it specifies that a certain numbered version -of the GNU Lesser General Public License "or any later version" -applies to it, you have the option of following the terms and -conditions either of that published version or of any later version -published by the Free Software Foundation. If the Library as you -received it does not specify a version number of the GNU Lesser -General Public License, you may choose any version of the GNU Lesser -General Public License ever published by the Free Software Foundation. - - If the Library as you received it specifies that a proxy can decide -whether future versions of the GNU Lesser General Public License shall -apply, that proxy's public statement of acceptance of any version is -permanent authorization for you to choose that version for the -Library. diff --git a/PortablePython/Lib/site-packages/pynput-1.8.1.dist-info/INSTALLER b/PortablePython/Lib/site-packages/pynput-1.8.1.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/PortablePython/Lib/site-packages/pynput-1.8.1.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/PortablePython/Lib/site-packages/pynput-1.8.1.dist-info/METADATA b/PortablePython/Lib/site-packages/pynput-1.8.1.dist-info/METADATA deleted file mode 100644 index fecb69e..0000000 --- a/PortablePython/Lib/site-packages/pynput-1.8.1.dist-info/METADATA +++ /dev/null @@ -1,914 +0,0 @@ -Metadata-Version: 2.1 -Name: pynput -Version: 1.8.1 -Summary: Monitor and control user input devices -Home-page: https://github.com/moses-palmer/pynput -Author: Moses Palmér -Author-email: moses.palmer@gmail.com -License: LGPLv3 -Keywords: control mouse,mouse input,control keyboard,keyboard input -Classifier: Development Status :: 5 - Production/Stable -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3) -Classifier: Operating System :: MacOS :: MacOS X -Classifier: Operating System :: Microsoft :: Windows :: Windows NT/2000 -Classifier: Operating System :: POSIX -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 2.7 -Classifier: Programming Language :: Python :: 3.4 -Classifier: Programming Language :: Python :: 3.5 -Classifier: Programming Language :: Python :: 3.6 -Classifier: Programming Language :: Python :: 3.7 -Classifier: Programming Language :: Python :: 3.8 -Classifier: Programming Language :: Python :: 3.9 -Classifier: Topic :: Software Development :: Libraries :: Python Modules -Classifier: Topic :: System :: Monitoring -License-File: COPYING.LGPL -Requires-Dist: six -Requires-Dist: evdev>=1.3; "linux" in sys_platform -Requires-Dist: python-xlib>=0.17; "linux" in sys_platform -Requires-Dist: enum34; python_version == "2.7" -Requires-Dist: pyobjc-framework-ApplicationServices>=8.0; sys_platform == "darwin" -Requires-Dist: pyobjc-framework-Quartz>=8.0; sys_platform == "darwin" - -pynput -====== - -This library allows you to control and monitor input devices. - -Currently, mouse and keyboard input and monitoring are supported. - -See `here `_ for the full -documentation. - - -Controlling the mouse ---------------------- - -Use ``pynput.mouse.Controller`` like this:: - - from pynput.mouse import Button, Controller - - mouse = Controller() - - # Read pointer position - print('The current pointer position is {}'.format( - mouse.position)) - - # Set pointer position - mouse.position = (10, 20) - print('Now we have moved it to {}'.format( - mouse.position)) - - # Move pointer relative to current position - mouse.move(5, -5) - - # Press and release - mouse.press(Button.left) - mouse.release(Button.left) - - # Double click; this is different from pressing and releasing - # twice on macOS - mouse.click(Button.left, 2) - - # Scroll two steps down - mouse.scroll(0, 2) - - -Monitoring the mouse --------------------- - -Use ``pynput.mouse.Listener`` like this:: - - from pynput import mouse - - def on_move(x, y, injected): - print('Pointer moved to {}; it was {}'.format( - (x, y, 'faked' if injected else 'not faked'))) - - def on_click(x, y, button, pressed, injected): - print('{} at {}; it was {}'.format( - 'Pressed' if pressed else 'Released', - (x, y, 'faked' if injected else 'not faked'))) - if not pressed: - # Stop listener - return False - - def on_scroll(x, y, dx, dy, injected): - print('Scrolled {} at {}; it was {}'.format( - 'down' if dy < 0 else 'up', - (x, y, 'faked' if injected else 'not faked'))) - - # Collect events until released - with mouse.Listener( - on_move=on_move, - on_click=on_click, - on_scroll=on_scroll) as listener: - listener.join() - - # ...or, in a non-blocking fashion: - listener = mouse.Listener( - on_move=on_move, - on_click=on_click, - on_scroll=on_scroll) - listener.start() - -A mouse listener is a ``threading.Thread``, and all callbacks will be invoked -from the thread. - -Call ``pynput.mouse.Listener.stop`` from anywhere, raise ``StopException`` or -return ``False`` from a callback to stop the listener. - -When using the non-blocking version above, the current thread will continue -executing. This might be necessary when integrating with other GUI frameworks -that incorporate a main-loop, but when run from a script, this will cause the -program to terminate immediately. - - -The mouse listener thread -~~~~~~~~~~~~~~~~~~~~~~~~~ - -The listener callbacks are invoked directly from an operating thread on some -platforms, notably *Windows*. - -This means that long running procedures and blocking operations should not be -invoked from the callback, as this risks freezing input for all processes. - -A possible workaround is to just dispatch incoming messages to a queue, and let -a separate thread handle them. - - -Handling mouse listener errors -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If a callback handler raises an exception, the listener will be stopped. Since -callbacks run in a dedicated thread, the exceptions will not automatically be -reraised. - -To be notified about callback errors, call ``Thread.join`` on the listener -instance:: - - from pynput import mouse - - class MyException(Exception): pass - - def on_click(x, y, button, pressed): - if button == mouse.Button.left: - raise MyException(button) - - # Collect events until released - with mouse.Listener( - on_click=on_click) as listener: - try: - listener.join() - except MyException as e: - print('{} was clicked'.format(e.args[0])) - - -Toggling event listening for the mouse listener -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Once ``pynput.mouse.Listener.stop`` has been called, the listener cannot be -restarted, since listeners are instances of ``threading.Thread``. - -If your application requires toggling listening events, you must either add an -internal flag to ignore events when not required, or create a new listener when -resuming listening. - - -Synchronous event listening for the mouse listener -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -To simplify scripting, synchronous event listening is supported through the -utility class ``pynput.mouse.Events``. This class supports reading single -events in a non-blocking fashion, as well as iterating over all events. - -To read a single event, use the following code:: - - from pynput import mouse - - # The event listener will be running in this block - with mouse.Events() as events: - # Block at most one second - event = events.get(1.0) - if event is None: - print('You did not interact with the mouse within one second') - else: - print('Received event {}'.format(event)) - -To iterate over mouse events, use the following code:: - - from pynput import mouse - - # The event listener will be running in this block - with mouse.Events() as events: - for event in events: - if event.button == mouse.Button.right: - break - else: - print('Received event {}'.format(event)) - -Please note that the iterator method does not support non-blocking operation, -so it will wait for at least one mouse event. - -The events will be instances of the inner classes found in -``pynput.mouse.Events``. - - -Ensuring consistent coordinates between listener and controller on Windows -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Recent versions of _Windows_ support running legacy applications scaled when -the system scaling has been increased beyond 100%. This allows old applications -to scale, albeit with a blurry look, and avoids tiny, unusable user interfaces. - -This scaling is unfortunately inconsistently applied to a mouse listener and a -controller: the listener will receive physical coordinates, but the controller -has to work with scaled coordinates. - -This can be worked around by telling Windows that your application is DPI -aware. This is a process global setting, so _pynput_ cannot do it -automatically. Do enable DPI awareness, run the following code:: - - import ctypes - - - PROCESS_PER_MONITOR_DPI_AWARE = 2 - - ctypes.windll.shcore.SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE) - - -Controlling the keyboard ------------------------- - -Use ``pynput.keyboard.Controller`` like this:: - - from pynput.keyboard import Key, Controller - - keyboard = Controller() - - # Press and release space - keyboard.press(Key.space) - keyboard.release(Key.space) - - # Type a lower case A; this will work even if no key on the - # physical keyboard is labelled 'A' - keyboard.press('a') - keyboard.release('a') - - # Type two upper case As - keyboard.press('A') - keyboard.release('A') - with keyboard.pressed(Key.shift): - keyboard.press('a') - keyboard.release('a') - - # Type 'Hello World' using the shortcut type method - keyboard.type('Hello World') - - -Monitoring the keyboard ------------------------ - -Use ``pynput.keyboard.Listener`` like this:: - - from pynput import keyboard - - def on_press(key, injected): - try: - print('alphanumeric key {} pressed; it was {}'.format( - key.char, 'faked' if injected else 'not faked')) - except AttributeError: - print('special key {} pressed'.format( - key)) - - def on_release(key, injected): - print('{} released; it was {}'.format( - key, 'faked' if injected else 'not faked')) - if key == keyboard.Key.esc: - # Stop listener - return False - - # Collect events until released - with keyboard.Listener( - on_press=on_press, - on_release=on_release) as listener: - listener.join() - - # ...or, in a non-blocking fashion: - listener = keyboard.Listener( - on_press=on_press, - on_release=on_release) - listener.start() - -A keyboard listener is a ``threading.Thread``, and all callbacks will be -invoked from the thread. - -Call ``pynput.keyboard.Listener.stop`` from anywhere, raise ``StopException`` -or return ``False`` from a callback to stop the listener. - -The ``key`` parameter passed to callbacks is a ``pynput.keyboard.Key``, for -special keys, a ``pynput.keyboard.KeyCode`` for normal alphanumeric keys, or -just ``None`` for unknown keys. - -When using the non-blocking version above, the current thread will continue -executing. This might be necessary when integrating with other GUI frameworks -that incorporate a main-loop, but when run from a script, this will cause the -program to terminate immediately. - - -The keyboard listener thread -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The listener callbacks are invoked directly from an operating thread on some -platforms, notably *Windows*. - -This means that long running procedures and blocking operations should not be -invoked from the callback, as this risks freezing input for all processes. - -A possible workaround is to just dispatch incoming messages to a queue, and let -a separate thread handle them. - - -Handling keyboard listener errors -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If a callback handler raises an exception, the listener will be stopped. Since -callbacks run in a dedicated thread, the exceptions will not automatically be -reraised. - -To be notified about callback errors, call ``Thread.join`` on the listener -instance:: - - from pynput import keyboard - - class MyException(Exception): pass - - def on_press(key): - if key == keyboard.Key.esc: - raise MyException(key) - - # Collect events until released - with keyboard.Listener( - on_press=on_press) as listener: - try: - listener.join() - except MyException as e: - print('{} was pressed'.format(e.args[0])) - - -Toggling event listening for the keyboard listener -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Once ``pynput.keyboard.Listener.stop`` has been called, the listener cannot be -restarted, since listeners are instances of ``threading.Thread``. - -If your application requires toggling listening events, you must either add an -internal flag to ignore events when not required, or create a new listener when -resuming listening. - - -Synchronous event listening for the keyboard listener -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -To simplify scripting, synchronous event listening is supported through the -utility class ``pynput.keyboard.Events``. This class supports reading single -events in a non-blocking fashion, as well as iterating over all events. - -To read a single event, use the following code:: - - from pynput import keyboard - - # The event listener will be running in this block - with keyboard.Events() as events: - # Block at most one second - event = events.get(1.0) - if event is None: - print('You did not press a key within one second') - else: - print('Received event {}'.format(event)) - -To iterate over keyboard events, use the following code:: - - from pynput import keyboard - - # The event listener will be running in this block - with keyboard.Events() as events: - for event in events: - if event.key == keyboard.Key.esc: - break - else: - print('Received event {}'.format(event)) - -Please note that the iterator method does not support non-blocking operation, -so it will wait for at least one keyboard event. - -The events will be instances of the inner classes found in -``pynput.keyboard.Events``. - - -Global hotkeys -~~~~~~~~~~~~~~ - -A common use case for keyboard monitors is reacting to global hotkeys. Since a -listener does not maintain any state, hotkeys involving multiple keys must -store this state somewhere. - -*pynput* provides the class ``pynput.keyboard.HotKey`` for this purpose. It -contains two methods to update the state, designed to be easily interoperable -with a keyboard listener: ``pynput.keyboard.HotKey.press`` and -``pynput.keyboard.HotKey.release`` which can be directly passed as listener -callbacks. - -The intended usage is as follows:: - - from pynput import keyboard - - def on_activate(): - print('Global hotkey activated!') - - def for_canonical(f): - return lambda k: f(l.canonical(k)) - - hotkey = keyboard.HotKey( - keyboard.HotKey.parse('++h'), - on_activate) - with keyboard.Listener( - on_press=for_canonical(hotkey.press), - on_release=for_canonical(hotkey.release)) as l: - l.join() - -This will create a hotkey, and then use a listener to update its state. Once -all the specified keys are pressed simultaneously, ``on_activate`` will be -invoked. - -Note that keys are passed through ``pynput.keyboard.Listener.canonical`` before -being passed to the ``HotKey`` instance. This is to remove any modifier state -from the key events, and to normalise modifiers with more than one physical -button. - -The method ``pynput.keyboard.HotKey.parse`` is a convenience function to -transform shortcut strings to key collections. Please see its documentation for -more information. - -To register a number of global hotkeys, use the convenience class -``pynput.keyboard.GlobalHotKeys``:: - - from pynput import keyboard - - def on_activate_h(): - print('++h pressed') - - def on_activate_i(): - print('++i pressed') - - with keyboard.GlobalHotKeys({ - '++h': on_activate_h, - '++i': on_activate_i}) as h: - h.join() - - -Release Notes -============= - -v1.8.1 (2025-03-17) - Various fixes ------------------------------------ -* Remove incorrectly merged line for the *Xorg* backend. Thanks to *sphh*! -* Let events know about the new ``injected`` parameter. Thanks to - *phpjunkie420*! - - -v1.8.0 (2025-03-03) - Allow detecting injected events ------------------------------------------------------ -* Add a flag to callbacks to allow detecting injected input events. -* Add ``media_stop`` key for *macOS*. Thanks to *laura-3*! -* Add ``eject`` key for *macOS*. Thanks to *DiMNDev*! - - -v1.7.8 (2025-02-28) - Fixes for Python 3.12 -------------------------------------------- -* Rename method for listeners to not conflict with new ``threading.Thread`` - field. - - -v1.7.7 (2024-05-10) - Various fixes ------------------------------------ -* Small corrections to the documentation. -* Handle explicit timeout when calling ``join`` on listeners. -* Correct regression in hot key handling for special keys. -* Reverted changes to lazy loading of ``CoreFoundation`` and ``Quartz``, since - this still does not appear to work. Thanks to *Zach Zaiman*! -* Let the type of values in ``Key`` be ``KeyCode`` so that type checkers are - not confused. Thanks to *Amund Eggen Svandal*! -* Do not crash in ``__del__`` on *Xorg* if display creation fails. Thanks to - *Gabriele Pongelli*! -* Correct support for emojis on *Windows*. Thanks to *Yunus Emre*! - - -v1.7.6 (2022-01-01) - Various fixes ------------------------------------ -* Allow passing virtual key codes to the parser for global hot keys. -* Stop the recording context asynchronously on *Xorg*. -* Do not pass ``None`` to ``objc.objc_object``. Thanks to *yejunxi*! -* Do not crash when pressing the *alt* key on *uinput*. Thanks to *Caldas - Lopes*! -* Use the correct option prefix for listeners derived from the backend - implementations. Thanks to *Yu Wang*! - - -v1.7.5 (2021-11-19) - Various fixes ------------------------------------ -* Corrected crashes on *Xorg* when a listener was configured to suppress - system events. Thanks to *jpramosi*! -* Improved handling of keyboard controller on *Windows*. The controller now - has a greater change of working with applications using lower level events. - Thanks to *bhudax*! -* Updated *macOS* implementation to use new version of *pyobjc*. - - -v1.7.4 (2021-10-10) - Various fixes ------------------------------------ -* Detect whether permissions are lacking on *macOS*. Thanks to *Dane Finlay*! -* Eagerly import symbols from ``CoreFoundation`` and ``Quartz``. Thanks to - *Ronald Oussoren*! -* Improved handling of ``dumpkeys`` utility. Thanks to *Markus Niedermann*! -* Removed ambiguous license file. - - -v1.7.3 (2021-02-10) - Various fixes ------------------------------------ -* Corrected *keysym* handling on *Xorg*; not all groups were loaded, and the - fallback to our internal tables was never triggered. Thanks to *Philipp - Klaus*! -* Updated the version of *Quartz* used for the *macOS* backend to allow - *pynput* to be installed on *Big Sur*. Thanks to *Michael Madden*! -* Added missing function keys on *Windows*. Thanks to *Dave Atkinson*! -* Corrected scroll speed for mouse controller on *macOS*. Thanks to *Albert - Zeyer*! -* Corrected media keys for *Xorg*. Thanks to *Gabriele N. Tornetta*! -* Corrected parameter name in documentation. Thanks to *Jinesi Yelizati*! - - -v1.7.2 (2020-12-21) - Corrected uinput key mapping --------------------------------------------------- -* Corrected mapping of virtual key codes to characters for the *uinput* - backend. -* Corrected spelling errors. Thanks to *Martin Michlmayr*! -* Corrected and improved documentation. - - -v1.7.1 (2020-08-30) - Corrected release notes ---------------------------------------------- -* Corrected thanks for arbitrary unicode character support for *Xorg*. - - -v1.7.0 (2020-08-30) - A new backend and many new features and bug fixes ------------------------------------------------------------------------ -* Added a new *uinput* based keyboard backend for *Linux*, when no *X* server - is available. -* Allow typing arbitrary unicode characters on *Xorg* backend. Thanks to - *gdiShun*! -* Allow overriding the automatically selected backend with an environment - variable, and added a dummy backend. -* Added support for mouse side button on *Windows*. Thanks to *danielkovarik*! -* Added convenience method to tap keys. -* Allow specifying raw virtual key codes in hotkeys. -* Improved error messages when a backend cannot be loaded. -* Include more information in stringification of events. -* Corrected return value of ``Events.get`` to that specified by the - documentation. -* Corrected keyboard listener not to type random characters on certain - keyboard layouts. -* Corrected errors when pressing certain keys on *Windows*, where the - operating system reports that they are dead but no combining version exists. -* Improved documentation. - - -v1.6.8 (2020-02-28) - Various fixes ------------------------------------ -* Updated documentation. -* Corrected lint warnings and tests. -* Do not use internal types in ``argtypes`` for ``win32`` functions; this - renders them uncallable for other code running in the same runtime. -* Include scan codes in events on *Windows*. Thanks to *bhudax*! -* Correctly apply transformation to scroll event values on *Windows*. Thanks - to *DOCCA0*! - - -v1.6.7 (2020-02-17) - Various fixes ------------------------------------ -* Corrected infinite scrolling on *macOS* when providing non-integer deltas. - Thanks to *Iván Munsuri Ibáñez*! -* Corrected controller and listener handling of media keys on *macOS*. Thanks - to *Iván Munsuri Ibáñez*! - - -v1.6.6 (2020-01-23) - Corrected hot key documentation ------------------------------------------------------ -* The code examples for the simple ``pynput.keyboard.HotKey`` now work. Thanks - to *jfongattw*! - - -v1.6.5 (2020-01-08) - Corrected media key mappings --------------------------------------------------- -* Corrected media key mappings on *macOS*. Thanks to *Luis Nachtigall*! - - -v1.6.4 (2020-01-03) - Corrected imports yet again -------------------------------------------------- -* Corrected imports for keyboard Controller. Thanks to *rhystedstone*! - - -v1.6.3 (2019-12-28) - Corrected imports again ---------------------------------------------- -* Corrected imports for keyboard Controller. Thanks to *Matt Iversen*! - - -v1.6.2 (2019-12-28) - Corrected imports ---------------------------------------- -* Corrected imports for keyboard Controller. Thanks to *Matt Iversen*! - - -v1.6.1 (2019-12-27) - Corrections for *Windows* ------------------------------------------------ -* Corrected global hotkeys on *Windows*. -* Corrected pressed / released state for keyboard listener on *Windows*. - Thanks to *segalion*! - -v1.6.0 (2019-12-11) - Global Hotkeys ------------------------------------- -* Added support for global hotkeys. -* Added support for streaming listener events synchronously. - - -v1.5.2 (2019-12-06) - Corrected media key names for *Xorg* ----------------------------------------------------------- -* Removed media flag from *Xorg* keys. - - -v1.5.1 (2019-12-06) - Corrected media key names for *macOS* ------------------------------------------------------------ -* Corrected attribute names for media keys on *macOS*. Thanks to *ah3243*! - - -v1.5.0 (2019-12-04) - Various improvements ------------------------------------------- -* Corrected keyboard listener on *Windows*. Thanks to *akiratakasaki*, - *segalion*, *SpecialCharacter*! -* Corrected handling of some special keys, including arrow keys, when combined - with modifiers on *Windows*. Thanks to *tuessetr*! -* Updated documentation to include information about DPI scaling on *Windows*. - Thanks to *david-szarka*! -* Added experimental support for media keys. Thanks to *ShivamJoker*, - *StormTersteeg*! - - -v1.4.5 (2019-11-05) - Corrected errors on *Python 3.8* ------------------------------------------------------- -* Corrected errors about using `in` operator for enums on *Python 3.8* on - *macOS*. - - -v1.4.4 (2019-09-24) - Actually corrected keyboard listener on macOS -------------------------------------------------------------------- -* Included commit to correctly fall back on - ``CGEventKeyboardGetUnicodeString``. -* Corrected deprecation warnings about ``Enum`` usage on *Python 3.8*. - - -v1.4.3 (2019-09-24) - Corrected keyboard listener on macOS again ----------------------------------------------------------------- -* Correctly fall back on ``CGEventKeyboardGetUnicodeString``. -* Updated documentation. - - -v1.4.2 (2019-03-22) - Corrected keyboard listener on macOS ----------------------------------------------------------- -* Use ``CGEventKeyboardGetUnicodeString`` in *macOS* keyboard listener to send - correct characters. -* Include keysym instead of key code in *Xorg* keyboard listener. -* Corrected logging to not include expected ``StopException``. -* Updated and corrected documentation. - - -v1.4.1 (2018-09-07) - Logging ------------------------------ -* Log unhandled exceptions raised by listener callbacks. - - -v1.4 (2018-07-03) - Event suppression -------------------------------------- -* Added possibility to fully suppress events when listening. -* Added support for typing some control characters. -* Added support for mouse drag events on *OSX*. Thanks to *jungledrum*! -* Include the key code in keyboard listener events. -* Correctly handle the numeric key pad on *Xorg* with *num lock* active. - Thanks to *TheoRet*! -* Corrected handling of current thread keyboard layout on *Windows*. Thanks to - *Schmettaling*! -* Corrected stopping of listeners on *Xorg*. -* Corrected import of ``Xlib.keysymdef.xkb`` on *Xorg*. Thanks to *Glandos*! - - -v1.3.10 (2018-02-05) - Do not crash under *Xephyr* --------------------------------------------------- -* Do not crash when ``Xlib.display.Display.get_input_focus`` returns an - integer, as it may when running under *Xephyr*. Thanks to *Eli Skeggs*! - - -v1.3.9 (2018-01-12) - Correctly handle the letter *A* on *OSX* --------------------------------------------------------------- -* Corrected check for virtual key code when generating keyboard events on - *OSX*. This fixes an issue where pressing *A* with *shift* explicitly pressed - would still type a minuscule letter. - - -v1.3.8 (2017-12-08) - Do not crash on some keyboard layouts on *OSX* --------------------------------------------------------------------- -* Fall back on a different method to retrieve the keyboard layout on *OSX*. - This helps for some keyboard layouts, such as *Chinese*. Thanks to - *haoflynet*! - - -v1.3.7 (2017-08-23) - *Xorg* corrections ----------------------------------------- -* Include mouse buttons up to *30* for *Xorg*. - - -v1.3.6 (2017-08-13) - *win32* corrections ------------------------------------------ -* Corrected double delivery of fake keyboard events on *Windows*. -* Corrected handling of synthetic unicode keys on *Windows*. - - -v1.3.5 (2017-06-07) - Corrected dependencies again --------------------------------------------------- -* Reverted changes in *1.3.3*. -* Corrected platform specifier for *Python 2* on *Linux*. - - -v1.3.4 (2017-06-05) - *Xorg* corrections ----------------------------------------- -* Corrected bounds check for values on *Xorg*. - - -v1.3.3 (2017-06-05) - Make dependencies non-optional ----------------------------------------------------- -* Made platform dependencies non-optional. - - -v1.3.2 (2017-05-15) - Fix for button click on Mac -------------------------------------------------- -* Corrected regression from previous release where button clicks would - crash the *Mac* mouse listener. - - -v1.3.1 (2017-05-12) - Fixes for unknown buttons on Linux --------------------------------------------------------- -* Fall back on `Button.unknown` for unknown mouse buttons in *Xorg* mouse - listener. - - -v1.3 (2017-04-10) - Platform specific features ----------------------------------------------- -* Added ability to stop event propagation on *Windows*. This will prevent - events from reaching other applications. -* Added ability to ignore events on *Windows*. This is a workaround for systems - where the keyboard monitor interferes with normal keyboard events. -* Added ability to modify events on *OSX*. This allows intercepting and - altering input events before they reach other applications. -* Corrected crash on *OSX* when some types of third party input sources are - installed. - - -v1.2 (2017-01-06) - Improved error handling -------------------------------------------- -* Allow catching exceptions thrown from listener callbacks. This changes the - API, as joining a listener now potentially raises unhandled exceptions, - and unhandled exceptions will stop listeners. -* Added support for the numeric keypad on *Linux*. -* Improved documentation. -* Thanks to *jollysean* and *gilleswijnker* for their input! - - -v1.1.7 (2017-01-02) - Handle middle button on Windows ------------------------------------------------------ -* Listen for and dispatch middle button mouse clicks on *Windows*. - - -v1.1.6 (2016-11-24) - Corrected context manager for pressing keys ------------------------------------------------------------------ -* Corrected bug in ``pynput.keyboard.Controller.pressed`` which caused it to - never release the key. Many thanks to Toby Southwell! - - -v1.1.5 (2016-11-17) - Corrected modifier key combinations on Linux ------------------------------------------------------------------- -* Corrected handling of modifier keys to allow them to be composable on - *Linux*. - - -v1.1.4 (2016-10-30) - Small bugfixes ------------------------------------- -* Corrected error generation when ``GetKeyboardState`` fails. -* Make sure to apply shift state to borrowed keys on *X*. -* Use *pylint*. - - -v1.1.3 (2016-09-27) - Changed Xlib backend library --------------------------------------------------- -* Changed *Xlib* library. - - -v1.1.2 (2016-09-26) - Added missing type for Python 2 ------------------------------------------------------ -* Added missing ``LPDWORD`` for *Python 2* on *Windows*. - - -v1.1.1 (2016-09-26) - Fixes for listeners and controllers on Windows --------------------------------------------------------------------- -* Corrected keyboard listener on *Windows*. Modifier keys and other keys - changing the state of the keyboard are now handled correctly. -* Corrected mouse click and release on *Windows*. -* Corrected code samples. - - -v1.1 (2016-06-22) - Simplified usage on Linux ---------------------------------------------- -* Propagate import errors raised on Linux to help troubleshoot missing - ``Xlib`` module. -* Declare ``python3-xlib`` as dependency on *Linux* for *Python 3*. - - -v1.0.6 (2016-04-19) - Universal wheel -------------------------------------- -* Make sure to build a universal wheel for all python versions. - - -v1.0.5 (2016-04-11) - Fixes for dragging on OSX ------------------------------------------------ -* Corrected dragging on *OSX*. -* Added scroll speed constant for *OSX* to correct slow scroll speed. - - -v1.0.4 (2016-04-11) - Fixes for clicking and scrolling on Windows ------------------------------------------------------------------ -* Corrected name of mouse input field when sending click and scroll events. - - -v1.0.3 (2016-04-05) - Fixes for Python 3 on Windows ---------------------------------------------------- -* Corrected use of ``ctypes`` on Windows. - - -v1.0.2 (2016-04-03) - Fixes for thread identifiers --------------------------------------------------- -* Use thread identifiers to identify threads, not Thread instances. - - -v1.0.1 (2016-04-03) - Fixes for Python 3 ----------------------------------------- -* Corrected bugs which prevented the library from being used on *Python 3*. - - -v1.0 (2016-02-28) - Stable Release ----------------------------------- -* Changed license to *LGPL*. -* Corrected minor bugs and inconsistencies. -* Corrected and extended documentation. - - -v0.6 (2016-02-08) - Keyboard Monitor ------------------------------------- -* Added support for monitoring the keyboard. -* Corrected wheel packaging. -* Corrected deadlock when stopping a listener in some cases on *X*. -* Corrected key code constants on *Mac OSX*. -* Do not intercept events on *Mac OSX*. - - -v0.5.1 (2016-01-26) - Do not die on dead keys ---------------------------------------------- -* Corrected handling of dead keys. -* Corrected documentation. - - -v0.5 (2016-01-18) - Keyboard Modifiers --------------------------------------- -* Added support for modifiers. - - -v0.4 (2015-12-22) - Keyboard Controller ---------------------------------------- -* Added keyboard controller. - - -v0.3 (2015-12-22) - Cleanup ---------------------------- -* Moved ``pynput.mouse.Controller.Button`` to top-level. - - -v0.2 (2015-10-28) - Initial Release ------------------------------------ -* Support for controlling the mouse on *Linux*, *Mac OSX* and *Windows*. -* Support for monitoring the mouse on *Linux*, *Mac OSX* and *Windows*. diff --git a/PortablePython/Lib/site-packages/pynput-1.8.1.dist-info/RECORD b/PortablePython/Lib/site-packages/pynput-1.8.1.dist-info/RECORD deleted file mode 100644 index f1c0319..0000000 --- a/PortablePython/Lib/site-packages/pynput-1.8.1.dist-info/RECORD +++ /dev/null @@ -1,55 +0,0 @@ -pynput-1.8.1.dist-info/COPYING.LGPL,sha256=eInlwsfJhthC1m5_bBVCQ1Mmf5nTUtL8MpjKfZxi0LU,7656 -pynput-1.8.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -pynput-1.8.1.dist-info/METADATA,sha256=YNPNdd5SDrz20GVbmn7XZ-viRHHlyyZu1rUhCoYTo0k,32018 -pynput-1.8.1.dist-info/RECORD,, -pynput-1.8.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -pynput-1.8.1.dist-info/WHEEL,sha256=qUzzGenXXuJTzyjFah76kDVqDvnk-YDzY00svnrl84w,109 -pynput-1.8.1.dist-info/pbr.json,sha256=CAnWerrCQ6A-ekJTVVKkD9J-ia4q-xoZzQWKOlPcseQ,47 -pynput-1.8.1.dist-info/top_level.txt,sha256=DpJjYf-VkYaa_COk_yUczD0pHqsLndB9SjmwcQGkXJQ,7 -pynput-1.8.1.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1 -pynput/__init__.py,sha256=dJ7uVZs3BRQILOxuM9BqDNDS36Qav123Hpx-3HUklpo,1334 -pynput/__pycache__/__init__.cpython-313.pyc,, -pynput/__pycache__/_info.cpython-313.pyc,, -pynput/_info.py,sha256=2aajH_7soMpmZO_oMDHp7R2aXpMher6jdVa5Mnhjj6g,775 -pynput/_util/__init__.py,sha256=ZB2LiSbGsbeQlnlVUQP9TNze15Fv6cb9m1zXYuJmy5g,15482 -pynput/_util/__pycache__/__init__.cpython-313.pyc,, -pynput/_util/__pycache__/darwin.cpython-313.pyc,, -pynput/_util/__pycache__/darwin_vks.cpython-313.pyc,, -pynput/_util/__pycache__/uinput.cpython-313.pyc,, -pynput/_util/__pycache__/win32.cpython-313.pyc,, -pynput/_util/__pycache__/win32_vks.cpython-313.pyc,, -pynput/_util/__pycache__/xorg.cpython-313.pyc,, -pynput/_util/__pycache__/xorg_keysyms.cpython-313.pyc,, -pynput/_util/darwin.py,sha256=AxtbSopLkKzWI1WLdCIZWMQtUtTuCxbRvWB6-WKcsCM,9200 -pynput/_util/darwin_vks.py,sha256=x-SbG4VgJ6Qc5-ZyF8cniqN3qIqahuQtXzG8ei94uWI,1512 -pynput/_util/uinput.py,sha256=ZFYiTsjQ7lr4h43X2Axrj-_FvWxiVT6dik7Z7Ra_sQE,2837 -pynput/_util/win32.py,sha256=YzEct1u1pzQ8v69kRlF-PnMB9BxwqkxCroNFVy4Ychw,18135 -pynput/_util/win32_vks.py,sha256=lC4y8E85nCGHmr4avVs44SvuBvUHBxmDeikNZcPGt6o,2894 -pynput/_util/xorg.py,sha256=ETHqMURfpvnA4q1UFIFQpWCj0c_9YTgasH3e0M4PRrM,15251 -pynput/_util/xorg_keysyms.py,sha256=YGRUXj1P0HT0gEkci2HZHyI_wcOwt0imt6R-7wPQVJo,69338 -pynput/keyboard/__init__.py,sha256=-5KBcWTSFjv2z1tu6sMV40UwhcqcX5O-yIex9fAmC_o,7903 -pynput/keyboard/__pycache__/__init__.cpython-313.pyc,, -pynput/keyboard/__pycache__/_base.cpython-313.pyc,, -pynput/keyboard/__pycache__/_darwin.cpython-313.pyc,, -pynput/keyboard/__pycache__/_dummy.cpython-313.pyc,, -pynput/keyboard/__pycache__/_uinput.cpython-313.pyc,, -pynput/keyboard/__pycache__/_win32.cpython-313.pyc,, -pynput/keyboard/__pycache__/_xorg.cpython-313.pyc,, -pynput/keyboard/_base.py,sha256=CPkWx8uE-oEhYMiRUY3uxqZuAMgT7Phrv3k8WoYp-D0,24082 -pynput/keyboard/_darwin.py,sha256=apF_nOR5EECuo1q-tZ5bi66JM3uFa83ZJy7Cfpc6xSQ,11672 -pynput/keyboard/_dummy.py,sha256=hkBFRY7HWyl86B_HdFiTeLthJjg926kN2peF-HUUXB0,895 -pynput/keyboard/_uinput.py,sha256=EH9FJX45IlbmjGvE1x4O2Ixvgkynp7XOka-vbtjVEAI,14479 -pynput/keyboard/_win32.py,sha256=i3S975s2SlEL9wva9ZMTi0icrNMWHyoj2zPN6gLeqsQ,12645 -pynput/keyboard/_xorg.py,sha256=QEGbet8NreQElzaSGfyARLaVoZxEvcIOTtjU6EHPnRQ,22677 -pynput/mouse/__init__.py,sha256=RiwZMZiWJEikRr8amiMoqJDVTn9i0irEfFcy3IA-s4w,2864 -pynput/mouse/__pycache__/__init__.cpython-313.pyc,, -pynput/mouse/__pycache__/_base.cpython-313.pyc,, -pynput/mouse/__pycache__/_darwin.cpython-313.pyc,, -pynput/mouse/__pycache__/_dummy.cpython-313.pyc,, -pynput/mouse/__pycache__/_win32.cpython-313.pyc,, -pynput/mouse/__pycache__/_xorg.cpython-313.pyc,, -pynput/mouse/_base.py,sha256=FfxHWA126ts1vz05YdcOHJOWb3XaF6WTctM-_ix6FNU,9338 -pynput/mouse/_darwin.py,sha256=a6M_Od7yCzEdxxrYGWLlHuInOWk6Nibd6dKnm5HC79s,6793 -pynput/mouse/_dummy.py,sha256=p16GreQcJbLMhE51uf8H3F8iarHf7Tu2s0JGt3J2xeQ,874 -pynput/mouse/_win32.py,sha256=e1NYKhsKe_Z2W4SZOJK3xWqDjtAa7Dy-zmvT_WzKx7M,7117 -pynput/mouse/_xorg.py,sha256=0P-N-jvRw0VTXH5P1O4LVnTds8jeHlqBiiDRpoUxVzQ,5559 diff --git a/PortablePython/Lib/site-packages/pynput-1.8.1.dist-info/REQUESTED b/PortablePython/Lib/site-packages/pynput-1.8.1.dist-info/REQUESTED deleted file mode 100644 index e69de29..0000000 diff --git a/PortablePython/Lib/site-packages/pynput-1.8.1.dist-info/WHEEL b/PortablePython/Lib/site-packages/pynput-1.8.1.dist-info/WHEEL deleted file mode 100644 index de294b9..0000000 --- a/PortablePython/Lib/site-packages/pynput-1.8.1.dist-info/WHEEL +++ /dev/null @@ -1,6 +0,0 @@ -Wheel-Version: 1.0 -Generator: setuptools (74.1.2) -Root-Is-Purelib: true -Tag: py2-none-any -Tag: py3-none-any - diff --git a/PortablePython/Lib/site-packages/pynput-1.8.1.dist-info/pbr.json b/PortablePython/Lib/site-packages/pynput-1.8.1.dist-info/pbr.json deleted file mode 100644 index ab50f33..0000000 --- a/PortablePython/Lib/site-packages/pynput-1.8.1.dist-info/pbr.json +++ /dev/null @@ -1 +0,0 @@ -{"is_release": false, "git_version": "2d6ab69"} \ No newline at end of file diff --git a/PortablePython/Lib/site-packages/pynput-1.8.1.dist-info/top_level.txt b/PortablePython/Lib/site-packages/pynput-1.8.1.dist-info/top_level.txt deleted file mode 100644 index 91a67a9..0000000 --- a/PortablePython/Lib/site-packages/pynput-1.8.1.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -pynput diff --git a/PortablePython/Lib/site-packages/pynput-1.8.1.dist-info/zip-safe b/PortablePython/Lib/site-packages/pynput-1.8.1.dist-info/zip-safe deleted file mode 100644 index 8b13789..0000000 --- a/PortablePython/Lib/site-packages/pynput-1.8.1.dist-info/zip-safe +++ /dev/null @@ -1 +0,0 @@ - diff --git a/PortablePython/Lib/site-packages/pynput/__init__.py b/PortablePython/Lib/site-packages/pynput/__init__.py deleted file mode 100644 index b6ab843..0000000 --- a/PortablePython/Lib/site-packages/pynput/__init__.py +++ /dev/null @@ -1,41 +0,0 @@ -# coding=utf-8 -# pynput -# Copyright (C) 2015-2024 Moses Palmér -# -# This program 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 3 of the License, or (at your option) any -# later version. -# -# This program 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 program. If not, see . -""" -The main *pynput* module. - -This module imports ``keyboard`` and ``mouse``. -""" - -def _logger(cls): - """Creates a logger with a name suitable for a specific class. - - This function takes into account that implementations for classes reside in - platform dependent modules, and thus removes the final part of the module - name. - - :param type cls: The class for which to create a logger. - - :return: a logger - """ - import logging - return logging.getLogger('{}.{}'.format( - '.'.join(cls.__module__.split('.', 2)[:2]), - cls.__name__)) - - -from . import keyboard -from . import mouse diff --git a/PortablePython/Lib/site-packages/pynput/_info.py b/PortablePython/Lib/site-packages/pynput/_info.py deleted file mode 100644 index 91bf033..0000000 --- a/PortablePython/Lib/site-packages/pynput/_info.py +++ /dev/null @@ -1,19 +0,0 @@ -# coding=utf-8 -# pystray -# Copyright (C) 2015-2024 Moses Palmér -# -# This program 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 3 of the License, or (at your option) any -# later version. -# -# This program 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 program. If not, see . - -__author__ = u'Moses Palmér' -__version__ = (1, 8, 1) diff --git a/PortablePython/Lib/site-packages/pynput/_util/__init__.py b/PortablePython/Lib/site-packages/pynput/_util/__init__.py deleted file mode 100644 index c7d98b4..0000000 --- a/PortablePython/Lib/site-packages/pynput/_util/__init__.py +++ /dev/null @@ -1,489 +0,0 @@ -# coding=utf-8 -# pynput -# Copyright (C) 2015-2024 Moses Palmér -# -# This program 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 3 of the License, or (at your option) any -# later version. -# -# This program 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 program. If not, see . -""" -General utility functions and classes. -""" - -# pylint: disable=R0903 -# We implement minimal mixins - -# pylint: disable=W0212 -# We implement an internal API - -import contextlib -import functools -import importlib -import inspect -import os -import sys -import threading -import time - -import six - -from six.moves import queue - - -#: Possible resolutions for import related errors. -RESOLUTIONS = { - 'darwin': 'Please make sure that you have Python bindings for the ' - 'system frameworks installed', - 'uinput': 'Please make sure that you are running as root, and that ' - 'the utility dumpkeys is installed', - 'xorg': 'Please make sure that you have an X server running, and that ' - 'the DISPLAY environment variable is set correctly'} - - -def backend(package): - """Returns the backend module for a package. - - :param str package: The package for which to load a backend. - """ - backend_name = os.environ.get( - 'PYNPUT_BACKEND_{}'.format(package.rsplit('.')[-1].upper()), - os.environ.get('PYNPUT_BACKEND', None)) - if backend_name: - modules = [backend_name] - elif sys.platform == 'darwin': - modules = ['darwin'] - elif sys.platform == 'win32': - modules = ['win32'] - else: - modules = ['xorg'] - - errors = [] - resolutions = [] - for module in modules: - try: - return importlib.import_module('._' + module, package) - except ImportError as e: - errors.append(e) - if module in RESOLUTIONS: - resolutions.append(RESOLUTIONS[module]) - - raise ImportError('this platform is not supported: {}'.format( - '; '.join(str(e) for e in errors)) + ('\n\n' - 'Try one of the following resolutions:\n\n' - + '\n\n'.join( - ' * {}'.format(s) - for s in resolutions)) - if resolutions else '') - - -def prefix(base, cls): - """Calculates the prefix to use for platform specific options for a - specific class. - - The prefix if the name of the module containing the class that is an - immediate subclass of ``base`` among the super classes of ``cls``. - """ - for super_cls in filter( - lambda cls: issubclass(cls, base), - cls.__mro__[1:]): - if super_cls is base: - return cls.__module__.rsplit('.', 1)[-1][1:] + '_' - else: - result = prefix(base, super_cls) - if result is not None: - return result - - -class AbstractListener(threading.Thread): - """A class implementing the basic behaviour for event listeners. - - Instances of this class can be used as context managers. This is equivalent - to the following code:: - - listener.start() - listener.wait() - try: - with_statements() - finally: - listener.stop() - - Actual implementations of this class must set the attribute ``_log``, which - must be an instance of :class:`logging.Logger`. - - :param bool suppress: Whether to suppress events. Setting this to ``True`` - will prevent the input events from being passed to the rest of the - system. - - :param kwargs: A mapping from callback attribute to callback handler. All - handlers will be wrapped in a function reading the return value of the - callback, and if it ``is False``, raising :class:`StopException`. - - Any callback that is falsy will be ignored. - """ - class StopException(Exception): - """If an event listener callback raises this exception, the current - listener is stopped. - """ - pass - - #: Exceptions that are handled outside of the emitter and should thus not - #: be passed through the queue - _HANDLED_EXCEPTIONS = tuple() - - def __init__(self, suppress=False, **kwargs): - super(AbstractListener, self).__init__() - - def wrapper(f): - def inner(*args): - if f(*args) is False: - raise self.StopException() - return inner - - self._suppress = suppress - self._running = False - self._thread = threading.current_thread() - self._condition = threading.Condition() - self._ready = False - - # Allow multiple calls to stop - self._queue = queue.Queue(10) - - self.daemon = True - - for name, callback in kwargs.items(): - setattr(self, name, wrapper(callback)) - - @property - def suppress(self): - """Whether to suppress events. - """ - return self._suppress - - @property - def running(self): - """Whether the listener is currently running. - """ - return self._running - - def stop(self): - """Stops listening for events. - - When this method returns, no more events will be delivered. Once this - method has been called, the listener instance cannot be used any more, - since a listener is a :class:`threading.Thread`, and once stopped it - cannot be restarted. - - To resume listening for event, a new listener must be created. - """ - if self._running: - self._running = False - self._queue.put(None) - self._stop_platform() - - def __enter__(self): - self.start() - self.wait() - return self - - def __exit__(self, exc_type, value, traceback): - self.stop() - - def wait(self): - """Waits for this listener to become ready. - """ - self._condition.acquire() - while not self._ready: - self._condition.wait() - self._condition.release() - - def run(self): - """The thread runner method. - """ - self._running = True - self._thread = threading.current_thread() - self._run() - - # Make sure that the queue contains something - self._queue.put(None) - - @classmethod - def _emitter(cls, f): - """A decorator to mark a method as the one emitting the callbacks. - - This decorator will wrap the method and catch exception. If a - :class:`StopException` is caught, the listener will be stopped - gracefully. If any other exception is caught, it will be propagated to - the thread calling :meth:`join` and reraised there. - """ - @functools.wraps(f) - def inner(self, *args, **kwargs): - # pylint: disable=W0702; we want to catch all exception - try: - return f(self, *args, **kwargs) - except Exception as e: - if not isinstance(e, self._HANDLED_EXCEPTIONS): - if not isinstance(e, AbstractListener.StopException): - self._log.exception( - 'Unhandled exception in listener callback') - self._queue.put( - None if isinstance(e, cls.StopException) - else sys.exc_info()) - self.stop() - raise - # pylint: enable=W0702 - - return inner - - def _mark_ready(self): - """Marks this listener as ready to receive events. - - This method must be called from :meth:`_run`. :meth:`wait` will block - until this method is called. - """ - self._condition.acquire() - self._ready = True - self._condition.notify() - self._condition.release() - - def _run(self): - """The implementation of the :meth:`run` method. - - This is a platform dependent implementation. - """ - raise NotImplementedError() - - def _stop_platform(self): - """The implementation of the :meth:`stop` method. - - This is a platform dependent implementation. - """ - raise NotImplementedError() - - def _wrap(self, f, args): - """Wraps a callable to make it accept ``args`` number of arguments. - - :param f: The callable to wrap. If this is ``None`` a no-op wrapper is - returned. - - :param int args: The number of arguments to accept. - - :raises ValueError: if f requires more than ``args`` arguments - """ - if f is None: - return lambda *a: None - else: - argspec = inspect.getfullargspec(f) - actual = len(inspect.signature(f).parameters) - defaults = len(argspec.defaults) if argspec.defaults else 0 - if actual - defaults > args: - raise ValueError(f) - elif actual >= args or argspec.varargs is not None: - return f - else: - return lambda *a: f(*a[:actual]) - - def join(self, timeout=None, *args): - start = time.time() - super(AbstractListener, self).join(timeout, *args) - timeout = max(0.0, timeout - (time.time() - start)) \ - if timeout is not None \ - else None - - # Reraise any exceptions; make sure not to block if a timeout was - # provided - try: - exc_type, exc_value, exc_traceback = self._queue.get( - timeout=timeout) - six.reraise(exc_type, exc_value, exc_traceback) - except queue.Empty: - pass - except TypeError: - return - - -class Events(object): - """A base class to enable iterating over events. - """ - #: The listener class providing events. - _Listener = None - - class Event(object): - def __str__(self): - return '{}({})'.format( - self.__class__.__name__, - ', '.join( - '{}={}'.format(k, v) - for (k, v) in vars(self).items())) - - def __eq__(self, other): - return self.__class__ == other.__class__ \ - and dir(self) == dir(other) \ - and all( - getattr(self, k) == getattr(other, k) - for k in dir(self)) - - def __init__(self, *args, **kwargs): - super(Events, self).__init__() - self._event_queue = queue.Queue() - self._sentinel = object() - self._listener = self._Listener(*args, **{ - key: self._event_mapper(value) - for (key, value) in kwargs.items()}) - self.start = self._listener.start - - def __enter__(self): - self._listener.__enter__() - return self - - def __exit__(self, *args): - self._listener.__exit__(*args) - - # Drain the queue to ensure that the put does not block - while True: - try: - self._event_queue.get_nowait() - except queue.Empty: - break - - self._event_queue.put(self._sentinel) - - def __iter__(self): - return self - - def __next__(self): - event = self.get() - if event is not None: - return event - else: - raise StopIteration() - - def get(self, timeout=None): - """Attempts to read the next event. - - :param int timeout: An optional timeout. If this is not provided, this - method may block infinitely. - - :return: the next event, or ``None`` if the source has been stopped or - no events were received - """ - try: - event = self._event_queue.get(timeout=timeout) - return event if event is not self._sentinel else None - except queue.Empty: - return None - - def _event_mapper(self, event): - """Generates an event callback to transforms the callback arguments to - an event and then publishes it. - - :param callback event: A function generating an event object. - - :return: a callback - """ - @functools.wraps(event) - def inner(*args): - try: - self._event_queue.put(event(*args), block=False) - except queue.Full: - pass - - return inner - - -class NotifierMixin(object): - """A mixin for notifiers of fake events. - - This mixin can be used for controllers on platforms where sending fake - events does not cause a listener to receive a notification. - """ - def _emit(self, action, *args): - """Sends a notification to all registered listeners. - - This method will ensure that listeners that raise - :class:`StopException` are stopped. - - :param str action: The name of the notification. - - :param args: The arguments to pass. - """ - stopped = [] - for listener in self._listeners(): - try: - getattr(listener, action)(*args) - except listener.StopException: - stopped.append(listener) - for listener in stopped: - listener.stop() - - @classmethod - def _receiver(cls, listener_class): - """A decorator to make a class able to receive fake events from a - controller. - - This decorator will add the method ``_receive`` to the decorated class. - - This method is a context manager which ensures that all calls to - :meth:`_emit` will invoke the named method in the listener instance - while the block is active. - """ - @contextlib.contextmanager - def receive(self): - """Executes a code block with this listener instance registered as - a receiver of fake input events. - """ - self._controller_class._add_listener(self) - try: - yield - finally: - self._controller_class._remove_listener(self) - - listener_class._receive = receive - listener_class._controller_class = cls - - # Make sure this class has the necessary attributes - if not hasattr(cls, '_listener_cache'): - cls._listener_cache = set() - cls._listener_lock = threading.Lock() - - return listener_class - - @classmethod - def _listeners(cls): - """Iterates over the set of running listeners. - - This method will quit without acquiring the lock if the set is empty, - so there is potential for race conditions. This is an optimisation, - since :class:`Controller` will need to call this method for every - control event. - """ - if not cls._listener_cache: - return - with cls._listener_lock: - for listener in cls._listener_cache: - yield listener - - @classmethod - def _add_listener(cls, listener): - """Adds a listener to the set of running listeners. - - :param listener: The listener for fake events. - """ - with cls._listener_lock: - cls._listener_cache.add(listener) - - @classmethod - def _remove_listener(cls, listener): - """Removes this listener from the set of running listeners. - - :param listener: The listener for fake events. - """ - with cls._listener_lock: - cls._listener_cache.remove(listener) diff --git a/PortablePython/Lib/site-packages/pynput/_util/darwin.py b/PortablePython/Lib/site-packages/pynput/_util/darwin.py deleted file mode 100644 index fdc284b..0000000 --- a/PortablePython/Lib/site-packages/pynput/_util/darwin.py +++ /dev/null @@ -1,302 +0,0 @@ -# coding=utf-8 -# pynput -# Copyright (C) 2015-2024 Moses Palmér -# -# This program 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 3 of the License, or (at your option) any -# later version. -# -# This program 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 program. If not, see . -""" -Utility functions and classes for the *Darwin* backend. -""" - -# pylint: disable=C0103 -# pylint: disable=R0903 -# This module contains wrapper classes - -import contextlib -import ctypes -import ctypes.util -import six - -import objc -import HIServices - -from CoreFoundation import ( - CFRelease -) - -from Quartz import ( - CFMachPortCreateRunLoopSource, - CFRunLoopAddSource, - CFRunLoopGetCurrent, - CFRunLoopRunInMode, - CFRunLoopStop, - CGEventGetIntegerValueField, - CGEventTapCreate, - CGEventTapEnable, - kCFRunLoopDefaultMode, - kCFRunLoopRunTimedOut, - kCGEventSourceUnixProcessID, - kCGEventTapOptionDefault, - kCGEventTapOptionListenOnly, - kCGHeadInsertEventTap, - kCGSessionEventTap) - - -from . import AbstractListener - - -def _wrap_value(value): - """Converts a pointer to a *Python objc* value. - - :param value: The pointer to convert. - - :return: a wrapped value - """ - return objc.objc_object(c_void_p=value) if value is not None else None - - -@contextlib.contextmanager -def _wrapped(value): - """A context manager that converts a raw pointer to a *Python objc* value. - - When the block is exited, the value is released. - - :param value: The raw value to wrap. - """ - wrapped_value = _wrap_value(value) - - try: - yield value - finally: - CFRelease(wrapped_value) - - -class CarbonExtra(object): - """A class exposing some missing functionality from *Carbon* as class - attributes. - """ - _Carbon = ctypes.cdll.LoadLibrary(ctypes.util.find_library('Carbon')) - - _Carbon.TISCopyCurrentKeyboardInputSource.argtypes = [] - _Carbon.TISCopyCurrentKeyboardInputSource.restype = ctypes.c_void_p - - _Carbon.TISCopyCurrentASCIICapableKeyboardLayoutInputSource.argtypes = [] - _Carbon.TISCopyCurrentASCIICapableKeyboardLayoutInputSource.restype = \ - ctypes.c_void_p - - _Carbon.TISGetInputSourceProperty.argtypes = [ - ctypes.c_void_p, ctypes.c_void_p] - _Carbon.TISGetInputSourceProperty.restype = ctypes.c_void_p - - _Carbon.LMGetKbdType.argtypes = [] - _Carbon.LMGetKbdType.restype = ctypes.c_uint32 - - _Carbon.UCKeyTranslate.argtypes = [ - ctypes.c_void_p, - ctypes.c_uint16, - ctypes.c_uint16, - ctypes.c_uint32, - ctypes.c_uint32, - ctypes.c_uint32, - ctypes.POINTER(ctypes.c_uint32), - ctypes.c_uint8, - ctypes.POINTER(ctypes.c_uint8), - ctypes.c_uint16 * 4] - _Carbon.UCKeyTranslate.restype = ctypes.c_uint32 - - TISCopyCurrentKeyboardInputSource = \ - _Carbon.TISCopyCurrentKeyboardInputSource - - TISCopyCurrentASCIICapableKeyboardLayoutInputSource = \ - _Carbon.TISCopyCurrentASCIICapableKeyboardLayoutInputSource - - kTISPropertyUnicodeKeyLayoutData = ctypes.c_void_p.in_dll( - _Carbon, 'kTISPropertyUnicodeKeyLayoutData') - - TISGetInputSourceProperty = \ - _Carbon.TISGetInputSourceProperty - - LMGetKbdType = \ - _Carbon.LMGetKbdType - - kUCKeyActionDisplay = 3 - kUCKeyTranslateNoDeadKeysBit = 0 - - UCKeyTranslate = \ - _Carbon.UCKeyTranslate - - -@contextlib.contextmanager -def keycode_context(): - """Returns an opaque value representing a context for translating keycodes - to strings. - """ - keyboard_type, layout_data = None, None - for source in [ - CarbonExtra.TISCopyCurrentKeyboardInputSource, - CarbonExtra.TISCopyCurrentASCIICapableKeyboardLayoutInputSource]: - with _wrapped(source()) as keyboard: - keyboard_type = CarbonExtra.LMGetKbdType() - layout = _wrap_value(CarbonExtra.TISGetInputSourceProperty( - keyboard, - CarbonExtra.kTISPropertyUnicodeKeyLayoutData)) - layout_data = layout.bytes().tobytes() if layout else None - if keyboard is not None and layout_data is not None: - break - yield (keyboard_type, layout_data) - - -def keycode_to_string(context, keycode, modifier_state=0): - """Converts a keycode to a string. - """ - LENGTH = 4 - - keyboard_type, layout_data = context - - dead_key_state = ctypes.c_uint32() - length = ctypes.c_uint8() - unicode_string = (ctypes.c_uint16 * LENGTH)() - CarbonExtra.UCKeyTranslate( - layout_data, - keycode, - CarbonExtra.kUCKeyActionDisplay, - modifier_state, - keyboard_type, - CarbonExtra.kUCKeyTranslateNoDeadKeysBit, - ctypes.byref(dead_key_state), - LENGTH, - ctypes.byref(length), - unicode_string) - return u''.join( - six.unichr(unicode_string[i]) - for i in range(length.value)) - - -def get_unicode_to_keycode_map(): - """Returns a mapping from unicode strings to virtual key codes. - - :return: a dict mapping key codes to strings - """ - with keycode_context() as context: - return { - keycode_to_string(context, keycode): keycode - for keycode in range(128)} - - -class ListenerMixin(object): - """A mixin for *Quartz* event listeners. - - Subclasses should set a value for :attr:`_EVENTS` and implement - :meth:`_handle_message`. - """ - #: The events that we listen to - _EVENTS = tuple() - - #: Whether this process is trusted to monitor input events. - IS_TRUSTED = False - - def _run(self): - self.IS_TRUSTED = HIServices.AXIsProcessTrusted() - if not self.IS_TRUSTED: - self._log.warning( - 'This process is not trusted! Input event monitoring will not ' - 'be possible until it is added to accessibility clients.') - - self._loop = None - try: - tap = self._create_event_tap() - if tap is None: - self._mark_ready() - return - - loop_source = CFMachPortCreateRunLoopSource( - None, tap, 0) - self._loop = CFRunLoopGetCurrent() - - CFRunLoopAddSource( - self._loop, loop_source, kCFRunLoopDefaultMode) - CGEventTapEnable(tap, True) - - self._mark_ready() - - # pylint: disable=W0702; we want to silence errors - try: - while self.running: - result = CFRunLoopRunInMode( - kCFRunLoopDefaultMode, 1, False) - try: - if result != kCFRunLoopRunTimedOut: - break - except AttributeError: - # This happens during teardown of the virtual machine - break - - except: - # This exception will have been passed to the main thread - pass - # pylint: enable=W0702 - - finally: - self._loop = None - - def _stop_platform(self): - # The base class sets the running flag to False; this will cause the - # loop around run loop invocations to terminate and set this event - try: - if self._loop is not None: - CFRunLoopStop(self._loop) - except AttributeError: - # The loop may not have been created - pass - - def _create_event_tap(self): - """Creates the event tap used by the listener. - - :return: an event tap - """ - return CGEventTapCreate( - kCGSessionEventTap, - kCGHeadInsertEventTap, - kCGEventTapOptionListenOnly if ( - True - and not self.suppress - and self._intercept is None) - else kCGEventTapOptionDefault, - self._EVENTS, - self._handler, - None) - - @AbstractListener._emitter - def _handler(self, proxy, event_type, event, refcon): - """The callback registered with *macOS* for mouse events. - - This method will call the callbacks registered on initialisation. - """ - # An injected event will have a Unix process ID attached - is_injected = (CGEventGetIntegerValueField( - event, - kCGEventSourceUnixProcessID)) != 0 - - self._handle_message(proxy, event_type, event, refcon, is_injected) - if self._intercept is not None: - return self._intercept(event_type, event) - elif self.suppress: - return None - - def _handle_message(self, proxy, event_type, event, refcon): - """The device specific callback handler. - - This method calls the appropriate callback registered when this - listener was created based on the event. - """ - raise NotImplementedError() diff --git a/PortablePython/Lib/site-packages/pynput/_util/darwin_vks.py b/PortablePython/Lib/site-packages/pynput/_util/darwin_vks.py deleted file mode 100644 index 6499fb1..0000000 --- a/PortablePython/Lib/site-packages/pynput/_util/darwin_vks.py +++ /dev/null @@ -1,79 +0,0 @@ -# coding: utf-8 -# pynput -# Copyright (C) 2015-2024 Moses Palmér -# -# This program 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 3 of the License, or (at your option) any -# later version. -# -# This program 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 program. If not, see . - -# pylint: disable=C0111,C0302 - -SYMBOLS = { - 0: 'a', - 1: 's', - 2: 'd', - 3: 'f', - 4: 'h', - 5: 'g', - 6: 'z', - 7: 'x', - 8: 'c', - 9: 'v', - 11: 'b', - 12: 'q', - 13: 'w', - 14: 'e', - 15: 'r', - 16: 'y', - 17: 't', - 18: '1', - 19: '2', - 20: '3', - 21: '4', - 22: '6', - 23: '5', - 24: '=', - 25: '9', - 26: '7', - 27: '-', - 28: '8', - 29: '0', - 30: ']', - 31: 'o', - 32: 'u', - 33: '[', - 34: 'i', - 35: 'p', - 37: 'l', - 38: 'j', - 39: '\'', - 40: 'k', - 41: ';', - 42: '\\', - 43: ',', - 44: '/', - 45: 'n', - 46: 'm', - 47: '.', - 49: ' ', - 50: '`', - 82: '0', - 83: '1', - 84: '2', - 85: '3', - 86: '4', - 87: '5', - 88: '6', - 89: '7', - 91: '8', - 92: '9', -} diff --git a/PortablePython/Lib/site-packages/pynput/_util/uinput.py b/PortablePython/Lib/site-packages/pynput/_util/uinput.py deleted file mode 100644 index b0a6a78..0000000 --- a/PortablePython/Lib/site-packages/pynput/_util/uinput.py +++ /dev/null @@ -1,99 +0,0 @@ -# coding=utf-8 -# pynput -# Copyright (C) 2015-2024 Moses Palmér -# -# This program 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 3 of the License, or (at your option) any -# later version. -# -# This program 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 program. If not, see . -""" -Utility functions and classes for the *uinput* backend. -""" - -# pylint: disable=R0903 -# We implement stubs - -import evdev - - -# Check that we have permissions to continue -def _check(): - # TODO: Implement! - pass -_check() -del _check - - -class ListenerMixin(object): - """A mixin for *uinput* event listeners. - - Subclasses should set a value for :attr:`_EVENTS` and implement - :meth:`_handle_message`. - """ - #: The events for which to listen - _EVENTS = tuple() - - def __init__(self, *args, **kwargs): - super(ListenerMixin, self).__init__(*args, **kwargs) - self._dev = self._device(self._options.get( - 'device_paths', - evdev.list_devices())) - if self.suppress: - self._dev.grab() - - def _run(self): - for event in self._dev.read_loop(): - if event.type in self._EVENTS: - self._handle_message(event) - - def _stop_platform(self): - self._dev.close() - - def _device(self, paths): - """Attempts to load a readable keyboard device. - - :param paths: A list of paths. - - :return: a compatible device - """ - dev, count = None, 0 - for path in paths: - # Open the device - try: - next_dev = evdev.InputDevice(path) - except OSError: - continue - - # Does this device provide more handled event codes? - capabilities = next_dev.capabilities() - next_count = sum( - len(codes) - for event, codes in capabilities.items() - if event in self._EVENTS) - if next_count > count: - dev = next_dev - count = next_count - else: - next_dev.close() - - if dev is None: - raise OSError('no keyboard device available') - else: - return dev - - def _handle_message(self, event): - """Handles a single event. - - This method should call one of the registered event callbacks. - - :param event: The event. - """ - raise NotImplementedError() diff --git a/PortablePython/Lib/site-packages/pynput/_util/win32.py b/PortablePython/Lib/site-packages/pynput/_util/win32.py deleted file mode 100644 index b945a59..0000000 --- a/PortablePython/Lib/site-packages/pynput/_util/win32.py +++ /dev/null @@ -1,598 +0,0 @@ -# coding=utf-8 -# pynput -# Copyright (C) 2015-2024 Moses Palmér -# -# This program 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 3 of the License, or (at your option) any -# later version. -# -# This program 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 program. If not, see . -""" -Utility functions and classes for the *win32* backend. -""" - -# pylint: disable=C0103 -# We want to make it obvious how structs are related - -# pylint: disable=R0903 -# This module contains a number of structs - -import contextlib -import ctypes -import itertools -import threading - -from ctypes import ( - windll, - wintypes) - -from . import AbstractListener, win32_vks as VK - - -# LPDWORD is not in ctypes.wintypes on Python 2 -if not hasattr(wintypes, 'LPDWORD'): - wintypes.LPDWORD = ctypes.POINTER(wintypes.DWORD) - - -class MOUSEINPUT(ctypes.Structure): - """Contains information about a simulated mouse event. - """ - MOVE = 0x0001 - LEFTDOWN = 0x0002 - LEFTUP = 0x0004 - RIGHTDOWN = 0x0008 - RIGHTUP = 0x0010 - MIDDLEDOWN = 0x0020 - MIDDLEUP = 0x0040 - XDOWN = 0x0080 - XUP = 0x0100 - WHEEL = 0x0800 - HWHEEL = 0x1000 - ABSOLUTE = 0x8000 - - XBUTTON1 = 0x0001 - XBUTTON2 = 0x0002 - - _fields_ = [ - ('dx', wintypes.LONG), - ('dy', wintypes.LONG), - ('mouseData', wintypes.DWORD), - ('dwFlags', wintypes.DWORD), - ('time', wintypes.DWORD), - ('dwExtraInfo', ctypes.c_void_p)] - - -class KEYBDINPUT(ctypes.Structure): - """Contains information about a simulated keyboard event. - """ - EXTENDEDKEY = 0x0001 - KEYUP = 0x0002 - SCANCODE = 0x0008 - UNICODE = 0x0004 - - _fields_ = [ - ('wVk', wintypes.WORD), - ('wScan', wintypes.WORD), - ('dwFlags', wintypes.DWORD), - ('time', wintypes.DWORD), - ('dwExtraInfo', ctypes.c_void_p)] - - -class HARDWAREINPUT(ctypes.Structure): - """Contains information about a simulated message generated by an input - device other than a keyboard or mouse. - """ - _fields_ = [ - ('uMsg', wintypes.DWORD), - ('wParamL', wintypes.WORD), - ('wParamH', wintypes.WORD)] - - -class INPUT_union(ctypes.Union): - """Represents the union of input types in :class:`INPUT`. - """ - _fields_ = [ - ('mi', MOUSEINPUT), - ('ki', KEYBDINPUT), - ('hi', HARDWAREINPUT)] - - -class INPUT(ctypes.Structure): - """Used by :attr:`SendInput` to store information for synthesizing input - events such as keystrokes, mouse movement, and mouse clicks. - """ - MOUSE = 0 - KEYBOARD = 1 - HARDWARE = 2 - - _fields_ = [ - ('type', wintypes.DWORD), - ('value', INPUT_union)] - - -LPINPUT = ctypes.POINTER(INPUT) - -VkKeyScan = windll.user32.VkKeyScanW -VkKeyScan.argtypes = ( - wintypes.WCHAR,) - -MapVirtualKey = windll.user32.MapVirtualKeyW -MapVirtualKey.argtypes = ( - wintypes.UINT, - wintypes.UINT) -MapVirtualKey.MAPVK_VK_TO_VSC = 0 - -SendInput = windll.user32.SendInput -SendInput.argtypes = ( - wintypes.UINT, - ctypes.c_voidp, # Really LPINPUT - ctypes.c_int) - -GetCurrentThreadId = windll.kernel32.GetCurrentThreadId -GetCurrentThreadId.restype = wintypes.DWORD - - -class MessageLoop(object): - """A class representing a message loop. - """ - #: The message that signals this loop to terminate - WM_STOP = 0x0401 - - _LPMSG = ctypes.POINTER(wintypes.MSG) - - _GetMessage = windll.user32.GetMessageW - _GetMessage.argtypes = ( - ctypes.c_voidp, # Really _LPMSG - wintypes.HWND, - wintypes.UINT, - wintypes.UINT) - _PeekMessage = windll.user32.PeekMessageW - _PeekMessage.argtypes = ( - ctypes.c_voidp, # Really _LPMSG - wintypes.HWND, - wintypes.UINT, - wintypes.UINT, - wintypes.UINT) - _PostThreadMessage = windll.user32.PostThreadMessageW - _PostThreadMessage.argtypes = ( - wintypes.DWORD, - wintypes.UINT, - wintypes.WPARAM, - wintypes.LPARAM) - - PM_NOREMOVE = 0 - - def __init__(self): - self._threadid = None - self._event = threading.Event() - self.thread = None - - def __iter__(self): - """Initialises the message loop and yields all messages until - :meth:`stop` is called. - - :raises AssertionError: if :meth:`start` has not been called - """ - assert self._threadid is not None - - try: - # Pump messages until WM_STOP - while True: - msg = wintypes.MSG() - lpmsg = ctypes.byref(msg) - r = self._GetMessage(lpmsg, None, 0, 0) - if r <= 0 or msg.message == self.WM_STOP: - break - else: - yield msg - - finally: - self._threadid = None - self.thread = None - - def start(self): - """Starts the message loop. - - This method must be called before iterating over messages, and it must - be called from the same thread. - """ - self._threadid = GetCurrentThreadId() - self.thread = threading.current_thread() - - # Create the message loop - msg = wintypes.MSG() - lpmsg = ctypes.byref(msg) - self._PeekMessage(lpmsg, None, 0x0400, 0x0400, self.PM_NOREMOVE) - - # Set the event to signal to other threads that the loop is created - self._event.set() - - def stop(self): - """Stops the message loop. - """ - self._event.wait() - if self._threadid: - self.post(self.WM_STOP, 0, 0) - - def post(self, msg, wparam, lparam): - """Posts a message to this message loop. - - :param ctypes.wintypes.UINT msg: The message. - - :param ctypes.wintypes.WPARAM wparam: The value of ``wParam``. - - :param ctypes.wintypes.LPARAM lparam: The value of ``lParam``. - """ - self._PostThreadMessage(self._threadid, msg, wparam, lparam) - - -class SystemHook(object): - """A class to handle Windows hooks. - """ - #: The hook action value for actions we should check - HC_ACTION = 0 - - _HOOKPROC = ctypes.WINFUNCTYPE( - wintypes.LPARAM, - ctypes.c_int32, wintypes.WPARAM, wintypes.LPARAM) - - _SetWindowsHookEx = windll.user32.SetWindowsHookExW - _SetWindowsHookEx.argtypes = ( - ctypes.c_int, - _HOOKPROC, - wintypes.HINSTANCE, - wintypes.DWORD) - _UnhookWindowsHookEx = windll.user32.UnhookWindowsHookEx - _UnhookWindowsHookEx.argtypes = ( - wintypes.HHOOK,) - _CallNextHookEx = windll.user32.CallNextHookEx - _CallNextHookEx.argtypes = ( - wintypes.HHOOK, - ctypes.c_int, - wintypes.WPARAM, - wintypes.LPARAM) - - #: The registered hook procedures - _HOOKS = {} - - class SuppressException(Exception): - """An exception raised by a hook callback to suppress further - propagation of events. - """ - pass - - def __init__(self, hook_id, on_hook=lambda code, msg, lpdata: None): - self.hook_id = hook_id - self.on_hook = on_hook - self._hook = None - - def __enter__(self): - key = threading.current_thread().ident - assert key not in self._HOOKS - - # Add ourself to lookup table and install the hook - self._HOOKS[key] = self - self._hook = self._SetWindowsHookEx( - self.hook_id, - self._handler, - None, - 0) - - return self - - def __exit__(self, exc_type, value, traceback): - key = threading.current_thread().ident - assert key in self._HOOKS - - if self._hook is not None: - # Uninstall the hook and remove ourself from lookup table - self._UnhookWindowsHookEx(self._hook) - del self._HOOKS[key] - - @staticmethod - @_HOOKPROC - def _handler(code, msg, lpdata): - key = threading.current_thread().ident - self = SystemHook._HOOKS.get(key, None) - if self: - # pylint: disable=W0702; we want to silence errors - try: - self.on_hook(code, msg, lpdata) - except self.SuppressException: - # Return non-zero to stop event propagation - return 1 - except: - # Ignore any errors - pass - # pylint: enable=W0702 - return SystemHook._CallNextHookEx(0, code, msg, lpdata) - - -class ListenerMixin(object): - """A mixin for *win32* event listeners. - - Subclasses should set a value for :attr:`_EVENTS` and implement - :meth:`_handle_message`. - - Subclasses must also be decorated with a decorator compatible with - :meth:`pynput._util.NotifierMixin._receiver` or implement the method - ``_receive()``. - """ - #: The Windows hook ID for the events to capture. - _EVENTS = None - - #: The window message used to signal that an even should be handled. - _WM_PROCESS = 0x410 - - #: Additional window messages to propagate to the subclass handler. - _WM_NOTIFICATIONS = [] - - def suppress_event(self): - """Causes the currently filtered event to be suppressed. - - This has a system wide effect and will generally result in no - applications receiving the event. - - This method will raise an undefined exception. - """ - raise SystemHook.SuppressException() - - def _run(self): - self._message_loop = MessageLoop() - with self._receive(): - self._mark_ready() - self._message_loop.start() - - # pylint: disable=W0702; we want to silence errors - try: - with SystemHook(self._EVENTS, self._handler): - # Just pump messages - for msg in self._message_loop: - if not self.running: - break - if msg.message == self._WM_PROCESS: - self._process(msg.wParam, msg.lParam) - elif msg.message in self._WM_NOTIFICATIONS: - self._on_notification( - msg.message, msg.wParam, msg.lParam) - except: - # This exception will have been passed to the main thread - pass - # pylint: enable=W0702 - - def _stop_platform(self): - try: - self._message_loop.stop() - except AttributeError: - # The loop may not have been created - pass - - @AbstractListener._emitter - def _handler(self, code, msg, lpdata): - """The callback registered with *Windows* for events. - - This method will post the message :attr:`_WM_PROCESS` to the message - loop started with this listener using :meth:`MessageLoop.post`. The - parameters are retrieved with a call to :meth:`_handle`. - """ - try: - converted = self._convert(code, msg, lpdata) - if converted is not None: - self._message_loop.post(self._WM_PROCESS, *converted) - except NotImplementedError: - self._handle_message(code, msg, lpdata) - - if self.suppress: - self.suppress_event() - - def _convert(self, code, msg, lpdata): - """The device specific callback handler. - - This method converts a low-level message and data to a - ``WPARAM`` / ``LPARAM`` pair. - """ - raise NotImplementedError() - - def _process(self, wparam, lparam): - """The device specific callback handler. - - This method performs the actual dispatching of events. - """ - raise NotImplementedError() - - def _handle_message(self, code, msg, lpdata): - """The device specific callback handler. - - This method calls the appropriate callback registered when this - listener was created based on the event. - - This method is only called if :meth:`_convert` is not implemented. - """ - raise NotImplementedError() - - def _on_notification(self, code, wparam, lparam): - """An additional notification handler. - - This method will be called for every message in - :attr:`_WM_NOTIFICATIONS`. - """ - raise NotImplementedError() - - -class KeyTranslator(object): - """A class to translate virtual key codes to characters. - """ - _GetAsyncKeyState = ctypes.windll.user32.GetAsyncKeyState - _GetAsyncKeyState.argtypes = ( - ctypes.c_int,) - _GetKeyboardLayout = ctypes.windll.user32.GetKeyboardLayout - _GetKeyboardLayout.argtypes = ( - wintypes.DWORD,) - _GetKeyboardState = ctypes.windll.user32.GetKeyboardState - _GetKeyboardState.argtypes = ( - ctypes.c_voidp,) - _GetKeyState = ctypes.windll.user32.GetAsyncKeyState - _GetKeyState.argtypes = ( - ctypes.c_int,) - _MapVirtualKeyEx = ctypes.windll.user32.MapVirtualKeyExW - _MapVirtualKeyEx.argtypes = ( - wintypes.UINT, - wintypes.UINT, - wintypes.HKL) - _ToUnicodeEx = ctypes.windll.user32.ToUnicodeEx - _ToUnicodeEx.argtypes = ( - wintypes.UINT, - wintypes.UINT, - ctypes.c_voidp, - ctypes.c_voidp, - ctypes.c_int, - wintypes.UINT, - wintypes.HKL) - - _MAPVK_VK_TO_VSC = 0 - _MAPVK_VSC_TO_VK = 1 - _MAPVK_VK_TO_CHAR = 2 - - def __init__(self): - self.update_layout() - - def __call__(self, vk, is_press): - """Converts a virtual key code to a string. - - :param int vk: The virtual key code. - - :param bool is_press: Whether this is a press. - - :return: parameters suitable for the :class:`pynput.keyboard.KeyCode` - constructor - - :raises OSError: if a call to any *win32* function fails - """ - # Get a string representation of the key - layout_data = self._layout_data[self._modifier_state()] - scan = self._to_scan(vk, self._layout) - character, is_dead = layout_data[scan] - - return { - 'char': character, - 'is_dead': is_dead, - 'vk': vk, - '_scan': scan} - - def update_layout(self): - """Updates the cached layout data. - """ - self._layout, self._layout_data = self._generate_layout() - - def char_from_scan(self, scan): - """Translates a scan code to a character, if possible. - - :param int scan: The scan code to translate. - - :return: maybe a character - :rtype: str or None - """ - return self._layout_data[(False, False, False)][scan][0] - - def _generate_layout(self): - """Generates the keyboard layout. - - This method will call ``ToUnicodeEx``, which modifies kernel buffers, - so it must *not* be called from the keyboard hook. - - The return value is the tuple ``(layout_handle, layout_data)``, where - ``layout_data`` is a mapping from the tuple ``(shift, ctrl, alt)`` to - an array indexed by scan code containing the data - ``(character, is_dead)``, and ``layout_handle`` is the handle of the - layout. - - :return: a composite layout - """ - layout_data = {} - - state = (ctypes.c_ubyte * 255)() - with self._thread_input() as active_thread: - layout = self._GetKeyboardLayout(active_thread) - vks = [ - self._to_vk(scan, layout) - for scan in range(len(state))] - - for shift, ctrl, alt in itertools.product( - (False, True), (False, True), (False, True)): - current = [(None, False)] * len(state) - layout_data[(shift, ctrl, alt)] = current - - # Update the keyboard state based on the modifier state - state[VK.SHIFT] = 0x80 if shift else 0x00 - state[VK.CONTROL] = 0x80 if ctrl else 0x00 - state[VK.MENU] = 0x80 if alt else 0x00 - - # For each virtual key code... - out = (ctypes.wintypes.WCHAR * 5)() - for (scan, vk) in enumerate(vks): - # ...translate it to a unicode character - count = self._ToUnicodeEx( - vk, scan, ctypes.byref(state), ctypes.byref(out), - len(out), 0, layout) - - # Cache the result if a key is mapped - if count != 0: - character = out[0] - is_dead = count < 0 - current[scan] = (character, is_dead) - - # If the key is dead, flush the keyboard state - if is_dead: - self._ToUnicodeEx( - vk, scan, ctypes.byref(state), - ctypes.byref(out), len(out), 0, layout) - - return (layout, layout_data) - - def _to_scan(self, vk, layout): - """Retrieves the scan code for a virtual key code. - - :param int vk: The virtual key code. - - :param layout: The keyboard layout. - - :return: the scan code - """ - return self._MapVirtualKeyEx( - vk, self._MAPVK_VK_TO_VSC, layout) - - def _to_vk(self, scan, layout): - """Retrieves the virtual key code for a scan code. - - :param int vscan: The scan code. - - :param layout: The keyboard layout. - - :return: the virtual key code - """ - return self._MapVirtualKeyEx( - scan, self._MAPVK_VSC_TO_VK, layout) - - def _modifier_state(self): - """Returns a key into :attr:`_layout_data` for the current modifier - state. - - :return: the current modifier state - """ - shift = bool(self._GetAsyncKeyState(VK.SHIFT) & 0x8000) - ctrl = bool(self._GetAsyncKeyState(VK.CONTROL) & 0x8000) - alt = bool(self._GetAsyncKeyState(VK.MENU) & 0x8000) - return (shift, ctrl, alt) - - @contextlib.contextmanager - def _thread_input(self): - """Yields the current thread ID. - """ - yield GetCurrentThreadId() diff --git a/PortablePython/Lib/site-packages/pynput/_util/win32_vks.py b/PortablePython/Lib/site-packages/pynput/_util/win32_vks.py deleted file mode 100644 index 7b30238..0000000 --- a/PortablePython/Lib/site-packages/pynput/_util/win32_vks.py +++ /dev/null @@ -1,179 +0,0 @@ -# coding: utf-8 -# pynput -# Copyright (C) 2015-2024 Moses Palmér -# -# This program 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 3 of the License, or (at your option) any -# later version. -# -# This program 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 program. If not, see . - -# pylint: disable=C0111,C0302 - -LBUTTON = 1 -RBUTTON = 2 -CANCEL = 3 -MBUTTON = 4 -XBUTTON1 = 5 -XBUTTON2 = 6 -BACK = 8 -TAB = 9 -CLEAR = 12 -RETURN = 13 -SHIFT = 16 -CONTROL = 17 -MENU = 18 -PAUSE = 19 -CAPITAL = 20 -KANA = 21 -HANGEUL = 21 -HANGUL = 21 -JUNJA = 23 -FINAL = 24 -HANJA = 25 -KANJI = 25 -ESCAPE = 27 -CONVERT = 28 -NONCONVERT = 29 -ACCEPT = 30 -MODECHANGE = 31 -SPACE = 32 -PRIOR = 33 -NEXT = 34 -END = 35 -HOME = 36 -LEFT = 37 -UP = 38 -RIGHT = 39 -DOWN = 40 -SELECT = 41 -PRINT = 42 -EXECUTE = 43 -SNAPSHOT = 44 -INSERT = 45 -DELETE = 46 -HELP = 47 -LWIN = 91 -RWIN = 92 -APPS = 93 -SLEEP = 95 -NUMPAD0 = 96 -NUMPAD1 = 97 -NUMPAD2 = 98 -NUMPAD3 = 99 -NUMPAD4 = 100 -NUMPAD5 = 101 -NUMPAD6 = 102 -NUMPAD7 = 103 -NUMPAD8 = 104 -NUMPAD9 = 105 -MULTIPLY = 106 -ADD = 107 -SEPARATOR = 108 -SUBTRACT = 109 -DECIMAL = 110 -DIVIDE = 111 -F1 = 112 -F2 = 113 -F3 = 114 -F4 = 115 -F5 = 116 -F6 = 117 -F7 = 118 -F8 = 119 -F9 = 120 -F10 = 121 -F11 = 122 -F12 = 123 -F13 = 124 -F14 = 125 -F15 = 126 -F16 = 127 -F17 = 128 -F18 = 129 -F19 = 130 -F20 = 131 -F21 = 132 -F22 = 133 -F23 = 134 -F24 = 135 -NUMLOCK = 144 -SCROLL = 145 -OEM_NEC_EQUAL = 146 -OEM_FJ_JISHO = 146 -OEM_FJ_MASSHOU = 147 -OEM_FJ_TOUROKU = 148 -OEM_FJ_LOYA = 149 -OEM_FJ_ROYA = 150 -LSHIFT = 160 -RSHIFT = 161 -LCONTROL = 162 -RCONTROL = 163 -LMENU = 164 -RMENU = 165 -BROWSER_BACK = 166 -BROWSER_FORWARD = 167 -BROWSER_REFRESH = 168 -BROWSER_STOP = 169 -BROWSER_SEARCH = 170 -BROWSER_FAVORITES = 171 -BROWSER_HOME = 172 -VOLUME_MUTE = 173 -VOLUME_DOWN = 174 -VOLUME_UP = 175 -MEDIA_NEXT_TRACK = 176 -MEDIA_PREV_TRACK = 177 -MEDIA_STOP = 178 -MEDIA_PLAY_PAUSE = 179 -LAUNCH_MAIL = 180 -LAUNCH_MEDIA_SELECT = 181 -LAUNCH_APP1 = 182 -LAUNCH_APP2 = 183 -OEM_1 = 186 -OEM_PLUS = 187 -OEM_COMMA = 188 -OEM_MINUS = 189 -OEM_PERIOD = 190 -OEM_2 = 191 -OEM_3 = 192 -OEM_4 = 219 -OEM_5 = 220 -OEM_6 = 221 -OEM_7 = 222 -OEM_8 = 223 -OEM_AX = 225 -OEM_102 = 226 -ICO_HELP = 227 -ICO_00 = 228 -PROCESSKEY = 229 -ICO_CLEAR = 230 -PACKET = 231 -OEM_RESET = 233 -OEM_JUMP = 234 -OEM_PA1 = 235 -OEM_PA2 = 236 -OEM_PA3 = 237 -OEM_WSCTRL = 238 -OEM_CUSEL = 239 -OEM_ATTN = 240 -OEM_FINISH = 241 -OEM_COPY = 242 -OEM_AUTO = 243 -OEM_ENLW = 244 -OEM_BACKTAB = 245 -ATTN = 246 -CRSEL = 247 -EXSEL = 248 -EREOF = 249 -PLAY = 250 -ZOOM = 251 -NONAME = 252 -PA1 = 253 -OEM_CLEAR = 254 diff --git a/PortablePython/Lib/site-packages/pynput/_util/xorg.py b/PortablePython/Lib/site-packages/pynput/_util/xorg.py deleted file mode 100644 index 52995f3..0000000 --- a/PortablePython/Lib/site-packages/pynput/_util/xorg.py +++ /dev/null @@ -1,496 +0,0 @@ -# coding=utf-8 -# pynput -# Copyright (C) 2015-2024 Moses Palmér -# -# This program 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 3 of the License, or (at your option) any -# later version. -# -# This program 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 program. If not, see . -""" -Utility functions and classes for the *Xorg* backend. -""" - -# pylint: disable=R0903 -# We implement stubs - -import contextlib -import functools -import itertools -import operator -import Xlib.display -import Xlib.keysymdef -import Xlib.threaded -import Xlib.XK - -from . import AbstractListener -from .xorg_keysyms import SYMBOLS - - -# Create a display to verify that we have an X connection -def _check_and_initialize(): - display = Xlib.display.Display() - display.close() - - for group in Xlib.keysymdef.__all__: - Xlib.XK.load_keysym_group(group) -_check_and_initialize() -del _check_and_initialize - - -class X11Error(Exception): - """An error that is thrown at the end of a code block managed by a - :func:`display_manager` if an *X11* error occurred. - """ - pass - - -@contextlib.contextmanager -def display_manager(display): - """Traps *X* errors and raises an :class:``X11Error`` at the end if any - error occurred. - - This handler also ensures that the :class:`Xlib.display.Display` being - managed is sync'd. - - :param Xlib.display.Display display: The *X* display. - - :return: the display - :rtype: Xlib.display.Display - """ - errors = [] - - def handler(*args): - """The *Xlib* error handler. - """ - errors.append(args) - - old_handler = display.set_error_handler(handler) - try: - yield display - display.sync() - finally: - display.set_error_handler(old_handler) - if errors: - raise X11Error(errors) - - -def _find_mask(display, symbol): - """Returns the mode flags to use for a modifier symbol. - - :param Xlib.display.Display display: The *X* display. - - :param str symbol: The name of the symbol. - - :return: the modifier mask - """ - # Get the key code for the symbol - modifier_keycode = display.keysym_to_keycode( - Xlib.XK.string_to_keysym(symbol)) - - for index, keycodes in enumerate(display.get_modifier_mapping()): - for keycode in keycodes: - if keycode == modifier_keycode: - return 1 << index - - return 0 - - -def alt_mask(display): - """Returns the *alt* mask flags. - - The first time this function is called for a display, the value is cached. - Subsequent calls will return the cached value. - - :param Xlib.display.Display display: The *X* display. - - :return: the modifier mask - """ - if not hasattr(display, '__alt_mask'): - display.__alt_mask = _find_mask(display, 'Alt_L') - return display.__alt_mask - - -def alt_gr_mask(display): - """Returns the *alt* mask flags. - - The first time this function is called for a display, the value is cached. - Subsequent calls will return the cached value. - - :param Xlib.display.Display display: The *X* display. - - :return: the modifier mask - """ - if not hasattr(display, '__altgr_mask'): - display.__altgr_mask = _find_mask(display, 'Mode_switch') - return display.__altgr_mask - - -def numlock_mask(display): - """Returns the *numlock* mask flags. - - The first time this function is called for a display, the value is cached. - Subsequent calls will return the cached value. - - :param Xlib.display.Display display: The *X* display. - - :return: the modifier mask - """ - if not hasattr(display, '__numlock_mask'): - display.__numlock_mask = _find_mask(display, 'Num_Lock') - return display.__numlock_mask - - -def keysym_is_latin_upper(keysym): - """Determines whether a *keysym* is an upper case *latin* character. - - This is true only if ``XK_A`` <= ``keysym`` <= ` XK_Z``. - - :param in keysym: The *keysym* to check. - """ - return Xlib.XK.XK_A <= keysym <= Xlib.XK.XK_Z - - -def keysym_is_latin_lower(keysym): - """Determines whether a *keysym* is a lower case *latin* character. - - This is true only if ``XK_a`` <= ``keysym`` <= ` XK_z``. - - :param in keysym: The *keysym* to check. - """ - return Xlib.XK.XK_a <= keysym <= Xlib.XK.XK_z - - -def keysym_group(ks1, ks2): - """Generates a group from two *keysyms*. - - The implementation of this function comes from: - - Within each group, if the second element of the group is ``NoSymbol``, - then the group should be treated as if the second element were the same - as the first element, except when the first element is an alphabetic - *KeySym* ``K`` for which both lowercase and uppercase forms are - defined. - - In that case, the group should be treated as if the first element were - the lowercase form of ``K`` and the second element were the uppercase - form of ``K``. - - This function assumes that *alphabetic* means *latin*; this assumption - appears to be consistent with observations of the return values from - ``XGetKeyboardMapping``. - - :param ks1: The first *keysym*. - - :param ks2: The second *keysym*. - - :return: a tuple conforming to the description above - """ - if ks2 == Xlib.XK.NoSymbol: - if keysym_is_latin_upper(ks1): - return (Xlib.XK.XK_a + ks1 - Xlib.XK.XK_A, ks1) - elif keysym_is_latin_lower(ks1): - return (ks1, Xlib.XK.XK_A + ks1 - Xlib.XK.XK_a) - else: - return (ks1, ks1) - else: - return (ks1, ks2) - - -def keysym_normalize(keysym): - """Normalises a list of *keysyms*. - - The implementation of this function comes from: - - If the list (ignoring trailing ``NoSymbol`` entries) is a single - *KeySym* ``K``, then the list is treated as if it were the list - ``K NoSymbol K NoSymbol``. - - If the list (ignoring trailing ``NoSymbol`` entries) is a pair of - *KeySyms* ``K1 K2``, then the list is treated as if it were the list - ``K1 K2 K1 K2``. - - If the list (ignoring trailing ``NoSymbol`` entries) is a triple of - *KeySyms* ``K1 K2 K3``, then the list is treated as if it were the list - ``K1 K2 K3 NoSymbol``. - - This function will also group the *keysyms* using :func:`keysym_group`. - - :param keysyms: A list of keysyms. - - :return: the tuple ``(group_1, group_2)`` or ``None`` - """ - # Remove trailing NoSymbol - stripped = list(reversed(list( - itertools.dropwhile( - lambda n: n == Xlib.XK.NoSymbol, - reversed(keysym))))) - - if not stripped: - return - - elif len(stripped) == 1: - return ( - keysym_group(stripped[0], Xlib.XK.NoSymbol), - keysym_group(stripped[0], Xlib.XK.NoSymbol)) - - elif len(stripped) == 2: - return ( - keysym_group(stripped[0], stripped[1]), - keysym_group(stripped[0], stripped[1])) - - elif len(stripped) == 3: - return ( - keysym_group(stripped[0], stripped[1]), - keysym_group(stripped[2], Xlib.XK.NoSymbol)) - - elif len(stripped) >= 6: - # TODO: Find out why this is necessary; using only the documented - # behaviour may lead to only a US layout being used? - return ( - keysym_group(stripped[0], stripped[1]), - keysym_group(stripped[4], stripped[5])) - - else: - return ( - keysym_group(stripped[0], stripped[1]), - keysym_group(stripped[2], stripped[3])) - - -def index_to_shift(display, index): - """Converts an index in a *key code* list to the corresponding shift state. - - :param Xlib.display.Display display: The display for which to retrieve the - shift mask. - - :param int index: The keyboard mapping *key code* index. - - :return: a shift mask - """ - return ( - (1 << 0 if index & 1 else 0) | - (alt_gr_mask(display) if index & 2 else 0)) - - -def shift_to_index(display, shift): - """Converts an index in a *key code* list to the corresponding shift state. - - :param Xlib.display.Display display: The display for which to retrieve the - shift mask. - - :param int index: The keyboard mapping *key code* index. - - :return: a shift mask - """ - return ( - (1 if shift & 1 else 0) + - (2 if shift & alt_gr_mask(display) else 0)) - - -def keyboard_mapping(display): - """Generates a mapping from *keysyms* to *key codes* and required - modifier shift states. - - :param Xlib.display.Display display: The display for which to retrieve the - keyboard mapping. - - :return: the keyboard mapping - """ - mapping = {} - - shift_mask = 1 << 0 - group_mask = alt_gr_mask(display) - - # Iterate over all keysym lists in the keyboard mapping - min_keycode = display.display.info.min_keycode - keycode_count = display.display.info.max_keycode - min_keycode + 1 - for index, keysyms in enumerate(display.get_keyboard_mapping( - min_keycode, keycode_count)): - key_code = index + min_keycode - - # Normalise the keysym list to yield a tuple containing the two groups - normalized = keysym_normalize(keysyms) - if not normalized: - continue - - # Iterate over the groups to extract the shift and modifier state - for groups, group in zip(normalized, (False, True)): - for keysym, shift in zip(groups, (False, True)): - if not keysym: - continue - shift_state = 0 \ - | (shift_mask if shift else 0) \ - | (group_mask if group else 0) - - # Prefer already known lesser shift states - if keysym in mapping and mapping[keysym][1] < shift_state: - continue - mapping[keysym] = (key_code, shift_state) - - return mapping - - -def char_to_keysym(char): - """Converts a unicode character to a *keysym*. - - :param str char: The unicode character. - - :return: the corresponding *keysym*, or ``0`` if it cannot be found - """ - ordinal = ord(char) - if ordinal < 0x100: - return ordinal - else: - return ordinal | 0x01000000 - - -def symbol_to_keysym(symbol): - """Converts a symbol name to a *keysym*. - - :param str symbol: The name of the symbol. - - :return: the corresponding *keysym*, or ``0`` if it cannot be found - """ - # First try simple translation, the try a module attribute of - # Xlib.keysymdef.xkb and fall back on our pre-generated table - return (0 - or Xlib.XK.string_to_keysym(symbol) - or getattr(Xlib.keysymdef.xkb, "XK_" + symbol, 0) - or SYMBOLS.get(symbol, (0,))[0]) - - -class ListenerMixin(object): - """A mixin for *X* event listeners. - - Subclasses should set a value for :attr:`_EVENTS` and implement - :meth:`_handle_message`. - """ - #: The events for which to listen - _EVENTS = tuple() - - #: We use this instance for parsing the binary data - _EVENT_PARSER = Xlib.protocol.rq.EventField(None) - - def _run(self): - self._display_stop = Xlib.display.Display() - self._display_record = Xlib.display.Display() - self._stopped = False - with display_manager(self._display_record) as dm: - self._context = dm.record_create_context( - 0, - [Xlib.ext.record.AllClients], - [{ - 'core_requests': (0, 0), - 'core_replies': (0, 0), - 'ext_requests': (0, 0, 0, 0), - 'ext_replies': (0, 0, 0, 0), - 'delivered_events': (0, 0), - 'device_events': self._EVENTS, - 'errors': (0, 0), - 'client_started': False, - 'client_died': False}]) - - # pylint: disable=W0702; we want to silence errors - try: - self._initialize(self._display_stop) - self._mark_ready() - if self.suppress: - with display_manager(self._display_stop) as dm: - self._suppress_start(dm) - self._display_record.record_enable_context( - self._context, self._handler) - except: - # This exception will have been passed to the main thread - pass - finally: - if self.suppress: - with display_manager(self._display_stop) as dm: - self._suppress_stop(dm) - self._display_stop.record_disable_context(self._context) - self._display_stop.flush() - self._display_record.record_free_context(self._context) - self._display_stop.close() - self._display_record.close() - # pylint: enable=W0702 - - def _stop_platform(self): - if not hasattr(self, '_context'): - self.wait() - - # Do this asynchronously to avoid deadlocks - self._display_record.record_disable_context(self._context) - - def _suppress_start(self, display): - """Starts suppressing events. - - :param Xlib.display.Display display: The display for which to suppress - events. - """ - raise NotImplementedError() - - def _suppress_stop(self, display): - """Starts suppressing events. - - :param Xlib.display.Display display: The display for which to suppress - events. - """ - raise NotImplementedError() - - @property - def _event_mask(self): - """The event mask. - """ - return functools.reduce(operator.__or__, self._EVENTS, 0) - - @AbstractListener._emitter - def _handler(self, events): - """The callback registered with *X* for mouse events. - - This method will parse the response and call the callbacks registered - on initialisation. - - :param events: The events passed by *X*. This is a binary block - parsable by :attr:`_EVENT_PARSER`. - """ - if not self.running: - raise self.StopException() - - data = events.data - - while data and len(data): - event, data = self._EVENT_PARSER.parse_binary_value( - data, self._display_record.display, None, None) - - injected = event.send_event - self._handle_message(self._display_stop, event, injected) - - def _initialize(self, display): - """Initialises this listener. - - This method is called immediately before the event loop, from the - handler thread. - - :param display: The display being used. - """ - pass - - def _handle_message(self, display, event, injected): - """The device specific callback handler. - - This method calls the appropriate callback registered when this - listener was created based on the event. - - :param display: The display being used. - - :param event: The event. - - :param bool injected: Whether the event was injected. - """ - pass diff --git a/PortablePython/Lib/site-packages/pynput/_util/xorg_keysyms.py b/PortablePython/Lib/site-packages/pynput/_util/xorg_keysyms.py deleted file mode 100644 index 7505e89..0000000 --- a/PortablePython/Lib/site-packages/pynput/_util/xorg_keysyms.py +++ /dev/null @@ -1,1715 +0,0 @@ -# coding: utf-8 -# pynput -# Copyright (C) 2015-2024 Moses Palmér -# -# This program 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 3 of the License, or (at your option) any -# later version. -# -# This program 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 program. If not, see . - -# pylint: disable=C0111,C0302 - -SYMBOLS = { - '0': (0x0030, u'\u0030'), - '1': (0x0031, u'\u0031'), - '2': (0x0032, u'\u0032'), - '3': (0x0033, u'\u0033'), - '4': (0x0034, u'\u0034'), - '5': (0x0035, u'\u0035'), - '6': (0x0036, u'\u0036'), - '7': (0x0037, u'\u0037'), - '8': (0x0038, u'\u0038'), - '9': (0x0039, u'\u0039'), - 'A': (0x0041, u'\u0041'), - 'AE': (0x00c6, u'\u00C6'), - 'Aacute': (0x00c1, u'\u00C1'), - 'Abelowdot': (0x1001ea0, u'\u1EA0'), - 'Abreve': (0x01c3, u'\u0102'), - 'Abreveacute': (0x1001eae, u'\u1EAE'), - 'Abrevebelowdot': (0x1001eb6, u'\u1EB6'), - 'Abrevegrave': (0x1001eb0, u'\u1EB0'), - 'Abrevehook': (0x1001eb2, u'\u1EB2'), - 'Abrevetilde': (0x1001eb4, u'\u1EB4'), - 'Acircumflex': (0x00c2, u'\u00C2'), - 'Acircumflexacute': (0x1001ea4, u'\u1EA4'), - 'Acircumflexbelowdot': (0x1001eac, u'\u1EAC'), - 'Acircumflexgrave': (0x1001ea6, u'\u1EA6'), - 'Acircumflexhook': (0x1001ea8, u'\u1EA8'), - 'Acircumflextilde': (0x1001eaa, u'\u1EAA'), - 'Adiaeresis': (0x00c4, u'\u00C4'), - 'Agrave': (0x00c0, u'\u00C0'), - 'Ahook': (0x1001ea2, u'\u1EA2'), - 'Amacron': (0x03c0, u'\u0100'), - 'Aogonek': (0x01a1, u'\u0104'), - 'Arabic_0': (0x1000660, u'\u0660'), - 'Arabic_1': (0x1000661, u'\u0661'), - 'Arabic_2': (0x1000662, u'\u0662'), - 'Arabic_3': (0x1000663, u'\u0663'), - 'Arabic_4': (0x1000664, u'\u0664'), - 'Arabic_5': (0x1000665, u'\u0665'), - 'Arabic_6': (0x1000666, u'\u0666'), - 'Arabic_7': (0x1000667, u'\u0667'), - 'Arabic_8': (0x1000668, u'\u0668'), - 'Arabic_9': (0x1000669, u'\u0669'), - 'Arabic_ain': (0x05d9, u'\u0639'), - 'Arabic_alef': (0x05c7, u'\u0627'), - 'Arabic_alefmaksura': (0x05e9, u'\u0649'), - 'Arabic_beh': (0x05c8, u'\u0628'), - 'Arabic_comma': (0x05ac, u'\u060C'), - 'Arabic_dad': (0x05d6, u'\u0636'), - 'Arabic_dal': (0x05cf, u'\u062F'), - 'Arabic_damma': (0x05ef, u'\u064F'), - 'Arabic_dammatan': (0x05ec, u'\u064C'), - 'Arabic_ddal': (0x1000688, u'\u0688'), - 'Arabic_farsi_yeh': (0x10006cc, u'\u06CC'), - 'Arabic_fatha': (0x05ee, u'\u064E'), - 'Arabic_fathatan': (0x05eb, u'\u064B'), - 'Arabic_feh': (0x05e1, u'\u0641'), - 'Arabic_fullstop': (0x10006d4, u'\u06D4'), - 'Arabic_gaf': (0x10006af, u'\u06AF'), - 'Arabic_ghain': (0x05da, u'\u063A'), - 'Arabic_ha': (0x05e7, u'\u0647'), - 'Arabic_hah': (0x05cd, u'\u062D'), - 'Arabic_hamza': (0x05c1, u'\u0621'), - 'Arabic_hamza_above': (0x1000654, u'\u0654'), - 'Arabic_hamza_below': (0x1000655, u'\u0655'), - 'Arabic_hamzaonalef': (0x05c3, u'\u0623'), - 'Arabic_hamzaonwaw': (0x05c4, u'\u0624'), - 'Arabic_hamzaonyeh': (0x05c6, u'\u0626'), - 'Arabic_hamzaunderalef': (0x05c5, u'\u0625'), - 'Arabic_heh_doachashmee': (0x10006be, u'\u06BE'), - 'Arabic_heh_goal': (0x10006c1, u'\u06C1'), - 'Arabic_jeem': (0x05cc, u'\u062C'), - 'Arabic_jeh': (0x1000698, u'\u0698'), - 'Arabic_kaf': (0x05e3, u'\u0643'), - 'Arabic_kasra': (0x05f0, u'\u0650'), - 'Arabic_kasratan': (0x05ed, u'\u064D'), - 'Arabic_keheh': (0x10006a9, u'\u06A9'), - 'Arabic_khah': (0x05ce, u'\u062E'), - 'Arabic_lam': (0x05e4, u'\u0644'), - 'Arabic_madda_above': (0x1000653, u'\u0653'), - 'Arabic_maddaonalef': (0x05c2, u'\u0622'), - 'Arabic_meem': (0x05e5, u'\u0645'), - 'Arabic_noon': (0x05e6, u'\u0646'), - 'Arabic_noon_ghunna': (0x10006ba, u'\u06BA'), - 'Arabic_peh': (0x100067e, u'\u067E'), - 'Arabic_percent': (0x100066a, u'\u066A'), - 'Arabic_qaf': (0x05e2, u'\u0642'), - 'Arabic_question_mark': (0x05bf, u'\u061F'), - 'Arabic_ra': (0x05d1, u'\u0631'), - 'Arabic_rreh': (0x1000691, u'\u0691'), - 'Arabic_sad': (0x05d5, u'\u0635'), - 'Arabic_seen': (0x05d3, u'\u0633'), - 'Arabic_semicolon': (0x05bb, u'\u061B'), - 'Arabic_shadda': (0x05f1, u'\u0651'), - 'Arabic_sheen': (0x05d4, u'\u0634'), - 'Arabic_sukun': (0x05f2, u'\u0652'), - 'Arabic_superscript_alef': (0x1000670, u'\u0670'), - 'Arabic_tah': (0x05d7, u'\u0637'), - 'Arabic_tatweel': (0x05e0, u'\u0640'), - 'Arabic_tcheh': (0x1000686, u'\u0686'), - 'Arabic_teh': (0x05ca, u'\u062A'), - 'Arabic_tehmarbuta': (0x05c9, u'\u0629'), - 'Arabic_thal': (0x05d0, u'\u0630'), - 'Arabic_theh': (0x05cb, u'\u062B'), - 'Arabic_tteh': (0x1000679, u'\u0679'), - 'Arabic_veh': (0x10006a4, u'\u06A4'), - 'Arabic_waw': (0x05e8, u'\u0648'), - 'Arabic_yeh': (0x05ea, u'\u064A'), - 'Arabic_yeh_baree': (0x10006d2, u'\u06D2'), - 'Arabic_zah': (0x05d8, u'\u0638'), - 'Arabic_zain': (0x05d2, u'\u0632'), - 'Aring': (0x00c5, u'\u00C5'), - 'Armenian_AT': (0x1000538, u'\u0538'), - 'Armenian_AYB': (0x1000531, u'\u0531'), - 'Armenian_BEN': (0x1000532, u'\u0532'), - 'Armenian_CHA': (0x1000549, u'\u0549'), - 'Armenian_DA': (0x1000534, u'\u0534'), - 'Armenian_DZA': (0x1000541, u'\u0541'), - 'Armenian_E': (0x1000537, u'\u0537'), - 'Armenian_FE': (0x1000556, u'\u0556'), - 'Armenian_GHAT': (0x1000542, u'\u0542'), - 'Armenian_GIM': (0x1000533, u'\u0533'), - 'Armenian_HI': (0x1000545, u'\u0545'), - 'Armenian_HO': (0x1000540, u'\u0540'), - 'Armenian_INI': (0x100053b, u'\u053B'), - 'Armenian_JE': (0x100054b, u'\u054B'), - 'Armenian_KE': (0x1000554, u'\u0554'), - 'Armenian_KEN': (0x100053f, u'\u053F'), - 'Armenian_KHE': (0x100053d, u'\u053D'), - 'Armenian_LYUN': (0x100053c, u'\u053C'), - 'Armenian_MEN': (0x1000544, u'\u0544'), - 'Armenian_NU': (0x1000546, u'\u0546'), - 'Armenian_O': (0x1000555, u'\u0555'), - 'Armenian_PE': (0x100054a, u'\u054A'), - 'Armenian_PYUR': (0x1000553, u'\u0553'), - 'Armenian_RA': (0x100054c, u'\u054C'), - 'Armenian_RE': (0x1000550, u'\u0550'), - 'Armenian_SE': (0x100054d, u'\u054D'), - 'Armenian_SHA': (0x1000547, u'\u0547'), - 'Armenian_TCHE': (0x1000543, u'\u0543'), - 'Armenian_TO': (0x1000539, u'\u0539'), - 'Armenian_TSA': (0x100053e, u'\u053E'), - 'Armenian_TSO': (0x1000551, u'\u0551'), - 'Armenian_TYUN': (0x100054f, u'\u054F'), - 'Armenian_VEV': (0x100054e, u'\u054E'), - 'Armenian_VO': (0x1000548, u'\u0548'), - 'Armenian_VYUN': (0x1000552, u'\u0552'), - 'Armenian_YECH': (0x1000535, u'\u0535'), - 'Armenian_ZA': (0x1000536, u'\u0536'), - 'Armenian_ZHE': (0x100053a, u'\u053A'), - 'Armenian_accent': (0x100055b, u'\u055B'), - 'Armenian_amanak': (0x100055c, u'\u055C'), - 'Armenian_apostrophe': (0x100055a, u'\u055A'), - 'Armenian_at': (0x1000568, u'\u0568'), - 'Armenian_ayb': (0x1000561, u'\u0561'), - 'Armenian_ben': (0x1000562, u'\u0562'), - 'Armenian_but': (0x100055d, u'\u055D'), - 'Armenian_cha': (0x1000579, u'\u0579'), - 'Armenian_da': (0x1000564, u'\u0564'), - 'Armenian_dza': (0x1000571, u'\u0571'), - 'Armenian_e': (0x1000567, u'\u0567'), - 'Armenian_exclam': (0x100055c, u'\u055C'), - 'Armenian_fe': (0x1000586, u'\u0586'), - 'Armenian_full_stop': (0x1000589, u'\u0589'), - 'Armenian_ghat': (0x1000572, u'\u0572'), - 'Armenian_gim': (0x1000563, u'\u0563'), - 'Armenian_hi': (0x1000575, u'\u0575'), - 'Armenian_ho': (0x1000570, u'\u0570'), - 'Armenian_hyphen': (0x100058a, u'\u058A'), - 'Armenian_ini': (0x100056b, u'\u056B'), - 'Armenian_je': (0x100057b, u'\u057B'), - 'Armenian_ke': (0x1000584, u'\u0584'), - 'Armenian_ken': (0x100056f, u'\u056F'), - 'Armenian_khe': (0x100056d, u'\u056D'), - 'Armenian_ligature_ew': (0x1000587, u'\u0587'), - 'Armenian_lyun': (0x100056c, u'\u056C'), - 'Armenian_men': (0x1000574, u'\u0574'), - 'Armenian_nu': (0x1000576, u'\u0576'), - 'Armenian_o': (0x1000585, u'\u0585'), - 'Armenian_paruyk': (0x100055e, u'\u055E'), - 'Armenian_pe': (0x100057a, u'\u057A'), - 'Armenian_pyur': (0x1000583, u'\u0583'), - 'Armenian_question': (0x100055e, u'\u055E'), - 'Armenian_ra': (0x100057c, u'\u057C'), - 'Armenian_re': (0x1000580, u'\u0580'), - 'Armenian_se': (0x100057d, u'\u057D'), - 'Armenian_separation_mark': (0x100055d, u'\u055D'), - 'Armenian_sha': (0x1000577, u'\u0577'), - 'Armenian_shesht': (0x100055b, u'\u055B'), - 'Armenian_tche': (0x1000573, u'\u0573'), - 'Armenian_to': (0x1000569, u'\u0569'), - 'Armenian_tsa': (0x100056e, u'\u056E'), - 'Armenian_tso': (0x1000581, u'\u0581'), - 'Armenian_tyun': (0x100057f, u'\u057F'), - 'Armenian_verjaket': (0x1000589, u'\u0589'), - 'Armenian_vev': (0x100057e, u'\u057E'), - 'Armenian_vo': (0x1000578, u'\u0578'), - 'Armenian_vyun': (0x1000582, u'\u0582'), - 'Armenian_yech': (0x1000565, u'\u0565'), - 'Armenian_yentamna': (0x100058a, u'\u058A'), - 'Armenian_za': (0x1000566, u'\u0566'), - 'Armenian_zhe': (0x100056a, u'\u056A'), - 'Atilde': (0x00c3, u'\u00C3'), - 'B': (0x0042, u'\u0042'), - 'Babovedot': (0x1001e02, u'\u1E02'), - 'Byelorussian_SHORTU': (0x06be, u'\u040E'), - 'Byelorussian_shortu': (0x06ae, u'\u045E'), - 'C': (0x0043, u'\u0043'), - 'Cabovedot': (0x02c5, u'\u010A'), - 'Cacute': (0x01c6, u'\u0106'), - 'Ccaron': (0x01c8, u'\u010C'), - 'Ccedilla': (0x00c7, u'\u00C7'), - 'Ccircumflex': (0x02c6, u'\u0108'), - 'ColonSign': (0x10020a1, u'\u20A1'), - 'CruzeiroSign': (0x10020a2, u'\u20A2'), - 'Cyrillic_A': (0x06e1, u'\u0410'), - 'Cyrillic_BE': (0x06e2, u'\u0411'), - 'Cyrillic_CHE': (0x06fe, u'\u0427'), - 'Cyrillic_CHE_descender': (0x10004b6, u'\u04B6'), - 'Cyrillic_CHE_vertstroke': (0x10004b8, u'\u04B8'), - 'Cyrillic_DE': (0x06e4, u'\u0414'), - 'Cyrillic_DZHE': (0x06bf, u'\u040F'), - 'Cyrillic_E': (0x06fc, u'\u042D'), - 'Cyrillic_EF': (0x06e6, u'\u0424'), - 'Cyrillic_EL': (0x06ec, u'\u041B'), - 'Cyrillic_EM': (0x06ed, u'\u041C'), - 'Cyrillic_EN': (0x06ee, u'\u041D'), - 'Cyrillic_EN_descender': (0x10004a2, u'\u04A2'), - 'Cyrillic_ER': (0x06f2, u'\u0420'), - 'Cyrillic_ES': (0x06f3, u'\u0421'), - 'Cyrillic_GHE': (0x06e7, u'\u0413'), - 'Cyrillic_GHE_bar': (0x1000492, u'\u0492'), - 'Cyrillic_HA': (0x06e8, u'\u0425'), - 'Cyrillic_HARDSIGN': (0x06ff, u'\u042A'), - 'Cyrillic_HA_descender': (0x10004b2, u'\u04B2'), - 'Cyrillic_I': (0x06e9, u'\u0418'), - 'Cyrillic_IE': (0x06e5, u'\u0415'), - 'Cyrillic_IO': (0x06b3, u'\u0401'), - 'Cyrillic_I_macron': (0x10004e2, u'\u04E2'), - 'Cyrillic_JE': (0x06b8, u'\u0408'), - 'Cyrillic_KA': (0x06eb, u'\u041A'), - 'Cyrillic_KA_descender': (0x100049a, u'\u049A'), - 'Cyrillic_KA_vertstroke': (0x100049c, u'\u049C'), - 'Cyrillic_LJE': (0x06b9, u'\u0409'), - 'Cyrillic_NJE': (0x06ba, u'\u040A'), - 'Cyrillic_O': (0x06ef, u'\u041E'), - 'Cyrillic_O_bar': (0x10004e8, u'\u04E8'), - 'Cyrillic_PE': (0x06f0, u'\u041F'), - 'Cyrillic_SCHWA': (0x10004d8, u'\u04D8'), - 'Cyrillic_SHA': (0x06fb, u'\u0428'), - 'Cyrillic_SHCHA': (0x06fd, u'\u0429'), - 'Cyrillic_SHHA': (0x10004ba, u'\u04BA'), - 'Cyrillic_SHORTI': (0x06ea, u'\u0419'), - 'Cyrillic_SOFTSIGN': (0x06f8, u'\u042C'), - 'Cyrillic_TE': (0x06f4, u'\u0422'), - 'Cyrillic_TSE': (0x06e3, u'\u0426'), - 'Cyrillic_U': (0x06f5, u'\u0423'), - 'Cyrillic_U_macron': (0x10004ee, u'\u04EE'), - 'Cyrillic_U_straight': (0x10004ae, u'\u04AE'), - 'Cyrillic_U_straight_bar': (0x10004b0, u'\u04B0'), - 'Cyrillic_VE': (0x06f7, u'\u0412'), - 'Cyrillic_YA': (0x06f1, u'\u042F'), - 'Cyrillic_YERU': (0x06f9, u'\u042B'), - 'Cyrillic_YU': (0x06e0, u'\u042E'), - 'Cyrillic_ZE': (0x06fa, u'\u0417'), - 'Cyrillic_ZHE': (0x06f6, u'\u0416'), - 'Cyrillic_ZHE_descender': (0x1000496, u'\u0496'), - 'Cyrillic_a': (0x06c1, u'\u0430'), - 'Cyrillic_be': (0x06c2, u'\u0431'), - 'Cyrillic_che': (0x06de, u'\u0447'), - 'Cyrillic_che_descender': (0x10004b7, u'\u04B7'), - 'Cyrillic_che_vertstroke': (0x10004b9, u'\u04B9'), - 'Cyrillic_de': (0x06c4, u'\u0434'), - 'Cyrillic_dzhe': (0x06af, u'\u045F'), - 'Cyrillic_e': (0x06dc, u'\u044D'), - 'Cyrillic_ef': (0x06c6, u'\u0444'), - 'Cyrillic_el': (0x06cc, u'\u043B'), - 'Cyrillic_em': (0x06cd, u'\u043C'), - 'Cyrillic_en': (0x06ce, u'\u043D'), - 'Cyrillic_en_descender': (0x10004a3, u'\u04A3'), - 'Cyrillic_er': (0x06d2, u'\u0440'), - 'Cyrillic_es': (0x06d3, u'\u0441'), - 'Cyrillic_ghe': (0x06c7, u'\u0433'), - 'Cyrillic_ghe_bar': (0x1000493, u'\u0493'), - 'Cyrillic_ha': (0x06c8, u'\u0445'), - 'Cyrillic_ha_descender': (0x10004b3, u'\u04B3'), - 'Cyrillic_hardsign': (0x06df, u'\u044A'), - 'Cyrillic_i': (0x06c9, u'\u0438'), - 'Cyrillic_i_macron': (0x10004e3, u'\u04E3'), - 'Cyrillic_ie': (0x06c5, u'\u0435'), - 'Cyrillic_io': (0x06a3, u'\u0451'), - 'Cyrillic_je': (0x06a8, u'\u0458'), - 'Cyrillic_ka': (0x06cb, u'\u043A'), - 'Cyrillic_ka_descender': (0x100049b, u'\u049B'), - 'Cyrillic_ka_vertstroke': (0x100049d, u'\u049D'), - 'Cyrillic_lje': (0x06a9, u'\u0459'), - 'Cyrillic_nje': (0x06aa, u'\u045A'), - 'Cyrillic_o': (0x06cf, u'\u043E'), - 'Cyrillic_o_bar': (0x10004e9, u'\u04E9'), - 'Cyrillic_pe': (0x06d0, u'\u043F'), - 'Cyrillic_schwa': (0x10004d9, u'\u04D9'), - 'Cyrillic_sha': (0x06db, u'\u0448'), - 'Cyrillic_shcha': (0x06dd, u'\u0449'), - 'Cyrillic_shha': (0x10004bb, u'\u04BB'), - 'Cyrillic_shorti': (0x06ca, u'\u0439'), - 'Cyrillic_softsign': (0x06d8, u'\u044C'), - 'Cyrillic_te': (0x06d4, u'\u0442'), - 'Cyrillic_tse': (0x06c3, u'\u0446'), - 'Cyrillic_u': (0x06d5, u'\u0443'), - 'Cyrillic_u_macron': (0x10004ef, u'\u04EF'), - 'Cyrillic_u_straight': (0x10004af, u'\u04AF'), - 'Cyrillic_u_straight_bar': (0x10004b1, u'\u04B1'), - 'Cyrillic_ve': (0x06d7, u'\u0432'), - 'Cyrillic_ya': (0x06d1, u'\u044F'), - 'Cyrillic_yeru': (0x06d9, u'\u044B'), - 'Cyrillic_yu': (0x06c0, u'\u044E'), - 'Cyrillic_ze': (0x06da, u'\u0437'), - 'Cyrillic_zhe': (0x06d6, u'\u0436'), - 'Cyrillic_zhe_descender': (0x1000497, u'\u0497'), - 'D': (0x0044, u'\u0044'), - 'Dabovedot': (0x1001e0a, u'\u1E0A'), - 'Dcaron': (0x01cf, u'\u010E'), - 'DongSign': (0x10020ab, u'\u20AB'), - 'Dstroke': (0x01d0, u'\u0110'), - 'E': (0x0045, u'\u0045'), - 'ENG': (0x03bd, u'\u014A'), - 'ETH': (0x00d0, u'\u00D0'), - 'EZH': (0x10001b7, u'\u01B7'), - 'Eabovedot': (0x03cc, u'\u0116'), - 'Eacute': (0x00c9, u'\u00C9'), - 'Ebelowdot': (0x1001eb8, u'\u1EB8'), - 'Ecaron': (0x01cc, u'\u011A'), - 'Ecircumflex': (0x00ca, u'\u00CA'), - 'Ecircumflexacute': (0x1001ebe, u'\u1EBE'), - 'Ecircumflexbelowdot': (0x1001ec6, u'\u1EC6'), - 'Ecircumflexgrave': (0x1001ec0, u'\u1EC0'), - 'Ecircumflexhook': (0x1001ec2, u'\u1EC2'), - 'Ecircumflextilde': (0x1001ec4, u'\u1EC4'), - 'EcuSign': (0x10020a0, u'\u20A0'), - 'Ediaeresis': (0x00cb, u'\u00CB'), - 'Egrave': (0x00c8, u'\u00C8'), - 'Ehook': (0x1001eba, u'\u1EBA'), - 'Emacron': (0x03aa, u'\u0112'), - 'Eogonek': (0x01ca, u'\u0118'), - 'Etilde': (0x1001ebc, u'\u1EBC'), - 'EuroSign': (0x20ac, u'\u20AC'), - 'F': (0x0046, u'\u0046'), - 'FFrancSign': (0x10020a3, u'\u20A3'), - 'Fabovedot': (0x1001e1e, u'\u1E1E'), - 'Farsi_0': (0x10006f0, u'\u06F0'), - 'Farsi_1': (0x10006f1, u'\u06F1'), - 'Farsi_2': (0x10006f2, u'\u06F2'), - 'Farsi_3': (0x10006f3, u'\u06F3'), - 'Farsi_4': (0x10006f4, u'\u06F4'), - 'Farsi_5': (0x10006f5, u'\u06F5'), - 'Farsi_6': (0x10006f6, u'\u06F6'), - 'Farsi_7': (0x10006f7, u'\u06F7'), - 'Farsi_8': (0x10006f8, u'\u06F8'), - 'Farsi_9': (0x10006f9, u'\u06F9'), - 'Farsi_yeh': (0x10006cc, u'\u06CC'), - 'G': (0x0047, u'\u0047'), - 'Gabovedot': (0x02d5, u'\u0120'), - 'Gbreve': (0x02ab, u'\u011E'), - 'Gcaron': (0x10001e6, u'\u01E6'), - 'Gcedilla': (0x03ab, u'\u0122'), - 'Gcircumflex': (0x02d8, u'\u011C'), - 'Georgian_an': (0x10010d0, u'\u10D0'), - 'Georgian_ban': (0x10010d1, u'\u10D1'), - 'Georgian_can': (0x10010ea, u'\u10EA'), - 'Georgian_char': (0x10010ed, u'\u10ED'), - 'Georgian_chin': (0x10010e9, u'\u10E9'), - 'Georgian_cil': (0x10010ec, u'\u10EC'), - 'Georgian_don': (0x10010d3, u'\u10D3'), - 'Georgian_en': (0x10010d4, u'\u10D4'), - 'Georgian_fi': (0x10010f6, u'\u10F6'), - 'Georgian_gan': (0x10010d2, u'\u10D2'), - 'Georgian_ghan': (0x10010e6, u'\u10E6'), - 'Georgian_hae': (0x10010f0, u'\u10F0'), - 'Georgian_har': (0x10010f4, u'\u10F4'), - 'Georgian_he': (0x10010f1, u'\u10F1'), - 'Georgian_hie': (0x10010f2, u'\u10F2'), - 'Georgian_hoe': (0x10010f5, u'\u10F5'), - 'Georgian_in': (0x10010d8, u'\u10D8'), - 'Georgian_jhan': (0x10010ef, u'\u10EF'), - 'Georgian_jil': (0x10010eb, u'\u10EB'), - 'Georgian_kan': (0x10010d9, u'\u10D9'), - 'Georgian_khar': (0x10010e5, u'\u10E5'), - 'Georgian_las': (0x10010da, u'\u10DA'), - 'Georgian_man': (0x10010db, u'\u10DB'), - 'Georgian_nar': (0x10010dc, u'\u10DC'), - 'Georgian_on': (0x10010dd, u'\u10DD'), - 'Georgian_par': (0x10010de, u'\u10DE'), - 'Georgian_phar': (0x10010e4, u'\u10E4'), - 'Georgian_qar': (0x10010e7, u'\u10E7'), - 'Georgian_rae': (0x10010e0, u'\u10E0'), - 'Georgian_san': (0x10010e1, u'\u10E1'), - 'Georgian_shin': (0x10010e8, u'\u10E8'), - 'Georgian_tan': (0x10010d7, u'\u10D7'), - 'Georgian_tar': (0x10010e2, u'\u10E2'), - 'Georgian_un': (0x10010e3, u'\u10E3'), - 'Georgian_vin': (0x10010d5, u'\u10D5'), - 'Georgian_we': (0x10010f3, u'\u10F3'), - 'Georgian_xan': (0x10010ee, u'\u10EE'), - 'Georgian_zen': (0x10010d6, u'\u10D6'), - 'Georgian_zhar': (0x10010df, u'\u10DF'), - 'Greek_ALPHA': (0x07c1, u'\u0391'), - 'Greek_ALPHAaccent': (0x07a1, u'\u0386'), - 'Greek_BETA': (0x07c2, u'\u0392'), - 'Greek_CHI': (0x07d7, u'\u03A7'), - 'Greek_DELTA': (0x07c4, u'\u0394'), - 'Greek_EPSILON': (0x07c5, u'\u0395'), - 'Greek_EPSILONaccent': (0x07a2, u'\u0388'), - 'Greek_ETA': (0x07c7, u'\u0397'), - 'Greek_ETAaccent': (0x07a3, u'\u0389'), - 'Greek_GAMMA': (0x07c3, u'\u0393'), - 'Greek_IOTA': (0x07c9, u'\u0399'), - 'Greek_IOTAaccent': (0x07a4, u'\u038A'), - 'Greek_IOTAdieresis': (0x07a5, u'\u03AA'), - 'Greek_KAPPA': (0x07ca, u'\u039A'), - 'Greek_LAMBDA': (0x07cb, u'\u039B'), - 'Greek_LAMDA': (0x07cb, u'\u039B'), - 'Greek_MU': (0x07cc, u'\u039C'), - 'Greek_NU': (0x07cd, u'\u039D'), - 'Greek_OMEGA': (0x07d9, u'\u03A9'), - 'Greek_OMEGAaccent': (0x07ab, u'\u038F'), - 'Greek_OMICRON': (0x07cf, u'\u039F'), - 'Greek_OMICRONaccent': (0x07a7, u'\u038C'), - 'Greek_PHI': (0x07d6, u'\u03A6'), - 'Greek_PI': (0x07d0, u'\u03A0'), - 'Greek_PSI': (0x07d8, u'\u03A8'), - 'Greek_RHO': (0x07d1, u'\u03A1'), - 'Greek_SIGMA': (0x07d2, u'\u03A3'), - 'Greek_TAU': (0x07d4, u'\u03A4'), - 'Greek_THETA': (0x07c8, u'\u0398'), - 'Greek_UPSILON': (0x07d5, u'\u03A5'), - 'Greek_UPSILONaccent': (0x07a8, u'\u038E'), - 'Greek_UPSILONdieresis': (0x07a9, u'\u03AB'), - 'Greek_XI': (0x07ce, u'\u039E'), - 'Greek_ZETA': (0x07c6, u'\u0396'), - 'Greek_accentdieresis': (0x07ae, u'\u0385'), - 'Greek_alpha': (0x07e1, u'\u03B1'), - 'Greek_alphaaccent': (0x07b1, u'\u03AC'), - 'Greek_beta': (0x07e2, u'\u03B2'), - 'Greek_chi': (0x07f7, u'\u03C7'), - 'Greek_delta': (0x07e4, u'\u03B4'), - 'Greek_epsilon': (0x07e5, u'\u03B5'), - 'Greek_epsilonaccent': (0x07b2, u'\u03AD'), - 'Greek_eta': (0x07e7, u'\u03B7'), - 'Greek_etaaccent': (0x07b3, u'\u03AE'), - 'Greek_finalsmallsigma': (0x07f3, u'\u03C2'), - 'Greek_gamma': (0x07e3, u'\u03B3'), - 'Greek_horizbar': (0x07af, u'\u2015'), - 'Greek_iota': (0x07e9, u'\u03B9'), - 'Greek_iotaaccent': (0x07b4, u'\u03AF'), - 'Greek_iotaaccentdieresis': (0x07b6, u'\u0390'), - 'Greek_iotadieresis': (0x07b5, u'\u03CA'), - 'Greek_kappa': (0x07ea, u'\u03BA'), - 'Greek_lambda': (0x07eb, u'\u03BB'), - 'Greek_lamda': (0x07eb, u'\u03BB'), - 'Greek_mu': (0x07ec, u'\u03BC'), - 'Greek_nu': (0x07ed, u'\u03BD'), - 'Greek_omega': (0x07f9, u'\u03C9'), - 'Greek_omegaaccent': (0x07bb, u'\u03CE'), - 'Greek_omicron': (0x07ef, u'\u03BF'), - 'Greek_omicronaccent': (0x07b7, u'\u03CC'), - 'Greek_phi': (0x07f6, u'\u03C6'), - 'Greek_pi': (0x07f0, u'\u03C0'), - 'Greek_psi': (0x07f8, u'\u03C8'), - 'Greek_rho': (0x07f1, u'\u03C1'), - 'Greek_sigma': (0x07f2, u'\u03C3'), - 'Greek_tau': (0x07f4, u'\u03C4'), - 'Greek_theta': (0x07e8, u'\u03B8'), - 'Greek_upsilon': (0x07f5, u'\u03C5'), - 'Greek_upsilonaccent': (0x07b8, u'\u03CD'), - 'Greek_upsilonaccentdieresis': (0x07ba, u'\u03B0'), - 'Greek_upsilondieresis': (0x07b9, u'\u03CB'), - 'Greek_xi': (0x07ee, u'\u03BE'), - 'Greek_zeta': (0x07e6, u'\u03B6'), - 'H': (0x0048, u'\u0048'), - 'Hcircumflex': (0x02a6, u'\u0124'), - 'Hstroke': (0x02a1, u'\u0126'), - 'I': (0x0049, u'\u0049'), - 'Iabovedot': (0x02a9, u'\u0130'), - 'Iacute': (0x00cd, u'\u00CD'), - 'Ibelowdot': (0x1001eca, u'\u1ECA'), - 'Ibreve': (0x100012c, u'\u012C'), - 'Icircumflex': (0x00ce, u'\u00CE'), - 'Idiaeresis': (0x00cf, u'\u00CF'), - 'Igrave': (0x00cc, u'\u00CC'), - 'Ihook': (0x1001ec8, u'\u1EC8'), - 'Imacron': (0x03cf, u'\u012A'), - 'Iogonek': (0x03c7, u'\u012E'), - 'Itilde': (0x03a5, u'\u0128'), - 'J': (0x004a, u'\u004A'), - 'Jcircumflex': (0x02ac, u'\u0134'), - 'K': (0x004b, u'\u004B'), - 'KP_0': (0xffb0, None), - 'KP_1': (0xffb1, None), - 'KP_2': (0xffb2, None), - 'KP_3': (0xffb3, None), - 'KP_4': (0xffb4, None), - 'KP_5': (0xffb5, None), - 'KP_6': (0xffb6, None), - 'KP_7': (0xffb7, None), - 'KP_8': (0xffb8, None), - 'KP_9': (0xffb9, None), - 'KP_Add': (0xffab, None), - 'KP_Begin': (0xff9d, None), - 'KP_Decimal': (0xffae, None), - 'KP_Delete': (0xff9f, None), - 'KP_Divide': (0xffaf, None), - 'KP_Down': (0xff99, None), - 'KP_End': (0xff9c, None), - 'KP_Enter': (0xff8d, None), - 'KP_Equal': (0xffbd, None), - 'KP_F1': (0xff91, None), - 'KP_F2': (0xff92, None), - 'KP_F3': (0xff93, None), - 'KP_F4': (0xff94, None), - 'KP_Home': (0xff95, None), - 'KP_Insert': (0xff9e, None), - 'KP_Left': (0xff96, None), - 'KP_Multiply': (0xffaa, None), - 'KP_Next': (0xff9b, None), - 'KP_Page_Down': (0xff9b, None), - 'KP_Page_Up': (0xff9a, None), - 'KP_Prior': (0xff9a, None), - 'KP_Right': (0xff98, None), - 'KP_Separator': (0xffac, None), - 'KP_Space': (0xff80, None), - 'KP_Subtract': (0xffad, None), - 'KP_Tab': (0xff89, None), - 'KP_Up': (0xff97, None), - 'Kcedilla': (0x03d3, u'\u0136'), - 'L': (0x004c, u'\u004C'), - 'Lacute': (0x01c5, u'\u0139'), - 'Lbelowdot': (0x1001e36, u'\u1E36'), - 'Lcaron': (0x01a5, u'\u013D'), - 'Lcedilla': (0x03a6, u'\u013B'), - 'LiraSign': (0x10020a4, u'\u20A4'), - 'Lstroke': (0x01a3, u'\u0141'), - 'M': (0x004d, u'\u004D'), - 'Mabovedot': (0x1001e40, u'\u1E40'), - 'Macedonia_DSE': (0x06b5, u'\u0405'), - 'Macedonia_GJE': (0x06b2, u'\u0403'), - 'Macedonia_KJE': (0x06bc, u'\u040C'), - 'Macedonia_dse': (0x06a5, u'\u0455'), - 'Macedonia_gje': (0x06a2, u'\u0453'), - 'Macedonia_kje': (0x06ac, u'\u045C'), - 'MillSign': (0x10020a5, u'\u20A5'), - 'N': (0x004e, u'\u004E'), - 'Nacute': (0x01d1, u'\u0143'), - 'NairaSign': (0x10020a6, u'\u20A6'), - 'Ncaron': (0x01d2, u'\u0147'), - 'Ncedilla': (0x03d1, u'\u0145'), - 'NewSheqelSign': (0x10020aa, u'\u20AA'), - 'Ntilde': (0x00d1, u'\u00D1'), - 'O': (0x004f, u'\u004F'), - 'OE': (0x13bc, u'\u0152'), - 'Oacute': (0x00d3, u'\u00D3'), - 'Obarred': (0x100019f, u'\u019F'), - 'Obelowdot': (0x1001ecc, u'\u1ECC'), - 'Ocaron': (0x10001d1, u'\u01D2'), - 'Ocircumflex': (0x00d4, u'\u00D4'), - 'Ocircumflexacute': (0x1001ed0, u'\u1ED0'), - 'Ocircumflexbelowdot': (0x1001ed8, u'\u1ED8'), - 'Ocircumflexgrave': (0x1001ed2, u'\u1ED2'), - 'Ocircumflexhook': (0x1001ed4, u'\u1ED4'), - 'Ocircumflextilde': (0x1001ed6, u'\u1ED6'), - 'Odiaeresis': (0x00d6, u'\u00D6'), - 'Odoubleacute': (0x01d5, u'\u0150'), - 'Ograve': (0x00d2, u'\u00D2'), - 'Ohook': (0x1001ece, u'\u1ECE'), - 'Ohorn': (0x10001a0, u'\u01A0'), - 'Ohornacute': (0x1001eda, u'\u1EDA'), - 'Ohornbelowdot': (0x1001ee2, u'\u1EE2'), - 'Ohorngrave': (0x1001edc, u'\u1EDC'), - 'Ohornhook': (0x1001ede, u'\u1EDE'), - 'Ohorntilde': (0x1001ee0, u'\u1EE0'), - 'Omacron': (0x03d2, u'\u014C'), - 'Ooblique': (0x00d8, u'\u00D8'), - 'Oslash': (0x00d8, u'\u00D8'), - 'Otilde': (0x00d5, u'\u00D5'), - 'P': (0x0050, u'\u0050'), - 'Pabovedot': (0x1001e56, u'\u1E56'), - 'PesetaSign': (0x10020a7, u'\u20A7'), - 'Q': (0x0051, u'\u0051'), - 'R': (0x0052, u'\u0052'), - 'Racute': (0x01c0, u'\u0154'), - 'Rcaron': (0x01d8, u'\u0158'), - 'Rcedilla': (0x03a3, u'\u0156'), - 'RupeeSign': (0x10020a8, u'\u20A8'), - 'S': (0x0053, u'\u0053'), - 'SCHWA': (0x100018f, u'\u018F'), - 'Sabovedot': (0x1001e60, u'\u1E60'), - 'Sacute': (0x01a6, u'\u015A'), - 'Scaron': (0x01a9, u'\u0160'), - 'Scedilla': (0x01aa, u'\u015E'), - 'Scircumflex': (0x02de, u'\u015C'), - 'Serbian_DJE': (0x06b1, u'\u0402'), - 'Serbian_TSHE': (0x06bb, u'\u040B'), - 'Serbian_dje': (0x06a1, u'\u0452'), - 'Serbian_tshe': (0x06ab, u'\u045B'), - 'Sinh_a': (0x1000d85, u'\u0D85'), - 'Sinh_aa': (0x1000d86, u'\u0D86'), - 'Sinh_aa2': (0x1000dcf, u'\u0DCF'), - 'Sinh_ae': (0x1000d87, u'\u0D87'), - 'Sinh_ae2': (0x1000dd0, u'\u0DD0'), - 'Sinh_aee': (0x1000d88, u'\u0D88'), - 'Sinh_aee2': (0x1000dd1, u'\u0DD1'), - 'Sinh_ai': (0x1000d93, u'\u0D93'), - 'Sinh_ai2': (0x1000ddb, u'\u0DDB'), - 'Sinh_al': (0x1000dca, u'\u0DCA'), - 'Sinh_au': (0x1000d96, u'\u0D96'), - 'Sinh_au2': (0x1000dde, u'\u0DDE'), - 'Sinh_ba': (0x1000db6, u'\u0DB6'), - 'Sinh_bha': (0x1000db7, u'\u0DB7'), - 'Sinh_ca': (0x1000da0, u'\u0DA0'), - 'Sinh_cha': (0x1000da1, u'\u0DA1'), - 'Sinh_dda': (0x1000da9, u'\u0DA9'), - 'Sinh_ddha': (0x1000daa, u'\u0DAA'), - 'Sinh_dha': (0x1000daf, u'\u0DAF'), - 'Sinh_dhha': (0x1000db0, u'\u0DB0'), - 'Sinh_e': (0x1000d91, u'\u0D91'), - 'Sinh_e2': (0x1000dd9, u'\u0DD9'), - 'Sinh_ee': (0x1000d92, u'\u0D92'), - 'Sinh_ee2': (0x1000dda, u'\u0DDA'), - 'Sinh_fa': (0x1000dc6, u'\u0DC6'), - 'Sinh_ga': (0x1000d9c, u'\u0D9C'), - 'Sinh_gha': (0x1000d9d, u'\u0D9D'), - 'Sinh_h2': (0x1000d83, u'\u0D83'), - 'Sinh_ha': (0x1000dc4, u'\u0DC4'), - 'Sinh_i': (0x1000d89, u'\u0D89'), - 'Sinh_i2': (0x1000dd2, u'\u0DD2'), - 'Sinh_ii': (0x1000d8a, u'\u0D8A'), - 'Sinh_ii2': (0x1000dd3, u'\u0DD3'), - 'Sinh_ja': (0x1000da2, u'\u0DA2'), - 'Sinh_jha': (0x1000da3, u'\u0DA3'), - 'Sinh_jnya': (0x1000da5, u'\u0DA5'), - 'Sinh_ka': (0x1000d9a, u'\u0D9A'), - 'Sinh_kha': (0x1000d9b, u'\u0D9B'), - 'Sinh_kunddaliya': (0x1000df4, u'\u0DF4'), - 'Sinh_la': (0x1000dbd, u'\u0DBD'), - 'Sinh_lla': (0x1000dc5, u'\u0DC5'), - 'Sinh_lu': (0x1000d8f, u'\u0D8F'), - 'Sinh_lu2': (0x1000ddf, u'\u0DDF'), - 'Sinh_luu': (0x1000d90, u'\u0D90'), - 'Sinh_luu2': (0x1000df3, u'\u0DF3'), - 'Sinh_ma': (0x1000db8, u'\u0DB8'), - 'Sinh_mba': (0x1000db9, u'\u0DB9'), - 'Sinh_na': (0x1000db1, u'\u0DB1'), - 'Sinh_ndda': (0x1000dac, u'\u0DAC'), - 'Sinh_ndha': (0x1000db3, u'\u0DB3'), - 'Sinh_ng': (0x1000d82, u'\u0D82'), - 'Sinh_ng2': (0x1000d9e, u'\u0D9E'), - 'Sinh_nga': (0x1000d9f, u'\u0D9F'), - 'Sinh_nja': (0x1000da6, u'\u0DA6'), - 'Sinh_nna': (0x1000dab, u'\u0DAB'), - 'Sinh_nya': (0x1000da4, u'\u0DA4'), - 'Sinh_o': (0x1000d94, u'\u0D94'), - 'Sinh_o2': (0x1000ddc, u'\u0DDC'), - 'Sinh_oo': (0x1000d95, u'\u0D95'), - 'Sinh_oo2': (0x1000ddd, u'\u0DDD'), - 'Sinh_pa': (0x1000db4, u'\u0DB4'), - 'Sinh_pha': (0x1000db5, u'\u0DB5'), - 'Sinh_ra': (0x1000dbb, u'\u0DBB'), - 'Sinh_ri': (0x1000d8d, u'\u0D8D'), - 'Sinh_rii': (0x1000d8e, u'\u0D8E'), - 'Sinh_ru2': (0x1000dd8, u'\u0DD8'), - 'Sinh_ruu2': (0x1000df2, u'\u0DF2'), - 'Sinh_sa': (0x1000dc3, u'\u0DC3'), - 'Sinh_sha': (0x1000dc1, u'\u0DC1'), - 'Sinh_ssha': (0x1000dc2, u'\u0DC2'), - 'Sinh_tha': (0x1000dad, u'\u0DAD'), - 'Sinh_thha': (0x1000dae, u'\u0DAE'), - 'Sinh_tta': (0x1000da7, u'\u0DA7'), - 'Sinh_ttha': (0x1000da8, u'\u0DA8'), - 'Sinh_u': (0x1000d8b, u'\u0D8B'), - 'Sinh_u2': (0x1000dd4, u'\u0DD4'), - 'Sinh_uu': (0x1000d8c, u'\u0D8C'), - 'Sinh_uu2': (0x1000dd6, u'\u0DD6'), - 'Sinh_va': (0x1000dc0, u'\u0DC0'), - 'Sinh_ya': (0x1000dba, u'\u0DBA'), - 'T': (0x0054, u'\u0054'), - 'THORN': (0x00de, u'\u00DE'), - 'Tabovedot': (0x1001e6a, u'\u1E6A'), - 'Tcaron': (0x01ab, u'\u0164'), - 'Tcedilla': (0x01de, u'\u0162'), - 'Thai_baht': (0x0ddf, u'\u0E3F'), - 'Thai_bobaimai': (0x0dba, u'\u0E1A'), - 'Thai_chochan': (0x0da8, u'\u0E08'), - 'Thai_chochang': (0x0daa, u'\u0E0A'), - 'Thai_choching': (0x0da9, u'\u0E09'), - 'Thai_chochoe': (0x0dac, u'\u0E0C'), - 'Thai_dochada': (0x0dae, u'\u0E0E'), - 'Thai_dodek': (0x0db4, u'\u0E14'), - 'Thai_fofa': (0x0dbd, u'\u0E1D'), - 'Thai_fofan': (0x0dbf, u'\u0E1F'), - 'Thai_hohip': (0x0dcb, u'\u0E2B'), - 'Thai_honokhuk': (0x0dce, u'\u0E2E'), - 'Thai_khokhai': (0x0da2, u'\u0E02'), - 'Thai_khokhon': (0x0da5, u'\u0E05'), - 'Thai_khokhuat': (0x0da3, u'\u0E03'), - 'Thai_khokhwai': (0x0da4, u'\u0E04'), - 'Thai_khorakhang': (0x0da6, u'\u0E06'), - 'Thai_kokai': (0x0da1, u'\u0E01'), - 'Thai_lakkhangyao': (0x0de5, u'\u0E45'), - 'Thai_lekchet': (0x0df7, u'\u0E57'), - 'Thai_lekha': (0x0df5, u'\u0E55'), - 'Thai_lekhok': (0x0df6, u'\u0E56'), - 'Thai_lekkao': (0x0df9, u'\u0E59'), - 'Thai_leknung': (0x0df1, u'\u0E51'), - 'Thai_lekpaet': (0x0df8, u'\u0E58'), - 'Thai_leksam': (0x0df3, u'\u0E53'), - 'Thai_leksi': (0x0df4, u'\u0E54'), - 'Thai_leksong': (0x0df2, u'\u0E52'), - 'Thai_leksun': (0x0df0, u'\u0E50'), - 'Thai_lochula': (0x0dcc, u'\u0E2C'), - 'Thai_loling': (0x0dc5, u'\u0E25'), - 'Thai_lu': (0x0dc6, u'\u0E26'), - 'Thai_maichattawa': (0x0deb, u'\u0E4B'), - 'Thai_maiek': (0x0de8, u'\u0E48'), - 'Thai_maihanakat': (0x0dd1, u'\u0E31'), - 'Thai_maitaikhu': (0x0de7, u'\u0E47'), - 'Thai_maitho': (0x0de9, u'\u0E49'), - 'Thai_maitri': (0x0dea, u'\u0E4A'), - 'Thai_maiyamok': (0x0de6, u'\u0E46'), - 'Thai_moma': (0x0dc1, u'\u0E21'), - 'Thai_ngongu': (0x0da7, u'\u0E07'), - 'Thai_nikhahit': (0x0ded, u'\u0E4D'), - 'Thai_nonen': (0x0db3, u'\u0E13'), - 'Thai_nonu': (0x0db9, u'\u0E19'), - 'Thai_oang': (0x0dcd, u'\u0E2D'), - 'Thai_paiyannoi': (0x0dcf, u'\u0E2F'), - 'Thai_phinthu': (0x0dda, u'\u0E3A'), - 'Thai_phophan': (0x0dbe, u'\u0E1E'), - 'Thai_phophung': (0x0dbc, u'\u0E1C'), - 'Thai_phosamphao': (0x0dc0, u'\u0E20'), - 'Thai_popla': (0x0dbb, u'\u0E1B'), - 'Thai_rorua': (0x0dc3, u'\u0E23'), - 'Thai_ru': (0x0dc4, u'\u0E24'), - 'Thai_saraa': (0x0dd0, u'\u0E30'), - 'Thai_saraaa': (0x0dd2, u'\u0E32'), - 'Thai_saraae': (0x0de1, u'\u0E41'), - 'Thai_saraaimaimalai': (0x0de4, u'\u0E44'), - 'Thai_saraaimaimuan': (0x0de3, u'\u0E43'), - 'Thai_saraam': (0x0dd3, u'\u0E33'), - 'Thai_sarae': (0x0de0, u'\u0E40'), - 'Thai_sarai': (0x0dd4, u'\u0E34'), - 'Thai_saraii': (0x0dd5, u'\u0E35'), - 'Thai_sarao': (0x0de2, u'\u0E42'), - 'Thai_sarau': (0x0dd8, u'\u0E38'), - 'Thai_saraue': (0x0dd6, u'\u0E36'), - 'Thai_sarauee': (0x0dd7, u'\u0E37'), - 'Thai_sarauu': (0x0dd9, u'\u0E39'), - 'Thai_sorusi': (0x0dc9, u'\u0E29'), - 'Thai_sosala': (0x0dc8, u'\u0E28'), - 'Thai_soso': (0x0dab, u'\u0E0B'), - 'Thai_sosua': (0x0dca, u'\u0E2A'), - 'Thai_thanthakhat': (0x0dec, u'\u0E4C'), - 'Thai_thonangmontho': (0x0db1, u'\u0E11'), - 'Thai_thophuthao': (0x0db2, u'\u0E12'), - 'Thai_thothahan': (0x0db7, u'\u0E17'), - 'Thai_thothan': (0x0db0, u'\u0E10'), - 'Thai_thothong': (0x0db8, u'\u0E18'), - 'Thai_thothung': (0x0db6, u'\u0E16'), - 'Thai_topatak': (0x0daf, u'\u0E0F'), - 'Thai_totao': (0x0db5, u'\u0E15'), - 'Thai_wowaen': (0x0dc7, u'\u0E27'), - 'Thai_yoyak': (0x0dc2, u'\u0E22'), - 'Thai_yoying': (0x0dad, u'\u0E0D'), - 'Tslash': (0x03ac, u'\u0166'), - 'U': (0x0055, u'\u0055'), - 'Uacute': (0x00da, u'\u00DA'), - 'Ubelowdot': (0x1001ee4, u'\u1EE4'), - 'Ubreve': (0x02dd, u'\u016C'), - 'Ucircumflex': (0x00db, u'\u00DB'), - 'Udiaeresis': (0x00dc, u'\u00DC'), - 'Udoubleacute': (0x01db, u'\u0170'), - 'Ugrave': (0x00d9, u'\u00D9'), - 'Uhook': (0x1001ee6, u'\u1EE6'), - 'Uhorn': (0x10001af, u'\u01AF'), - 'Uhornacute': (0x1001ee8, u'\u1EE8'), - 'Uhornbelowdot': (0x1001ef0, u'\u1EF0'), - 'Uhorngrave': (0x1001eea, u'\u1EEA'), - 'Uhornhook': (0x1001eec, u'\u1EEC'), - 'Uhorntilde': (0x1001eee, u'\u1EEE'), - 'Ukrainian_GHE_WITH_UPTURN': (0x06bd, u'\u0490'), - 'Ukrainian_I': (0x06b6, u'\u0406'), - 'Ukrainian_IE': (0x06b4, u'\u0404'), - 'Ukrainian_YI': (0x06b7, u'\u0407'), - 'Ukrainian_ghe_with_upturn': (0x06ad, u'\u0491'), - 'Ukrainian_i': (0x06a6, u'\u0456'), - 'Ukrainian_ie': (0x06a4, u'\u0454'), - 'Ukrainian_yi': (0x06a7, u'\u0457'), - 'Umacron': (0x03de, u'\u016A'), - 'Uogonek': (0x03d9, u'\u0172'), - 'Uring': (0x01d9, u'\u016E'), - 'Utilde': (0x03dd, u'\u0168'), - 'V': (0x0056, u'\u0056'), - 'W': (0x0057, u'\u0057'), - 'Wacute': (0x1001e82, u'\u1E82'), - 'Wcircumflex': (0x1000174, u'\u0174'), - 'Wdiaeresis': (0x1001e84, u'\u1E84'), - 'Wgrave': (0x1001e80, u'\u1E80'), - 'WonSign': (0x10020a9, u'\u20A9'), - 'X': (0x0058, u'\u0058'), - 'Xabovedot': (0x1001e8a, u'\u1E8A'), - 'Y': (0x0059, u'\u0059'), - 'Yacute': (0x00dd, u'\u00DD'), - 'Ybelowdot': (0x1001ef4, u'\u1EF4'), - 'Ycircumflex': (0x1000176, u'\u0176'), - 'Ydiaeresis': (0x13be, u'\u0178'), - 'Ygrave': (0x1001ef2, u'\u1EF2'), - 'Yhook': (0x1001ef6, u'\u1EF6'), - 'Ytilde': (0x1001ef8, u'\u1EF8'), - 'Z': (0x005a, u'\u005A'), - 'Zabovedot': (0x01af, u'\u017B'), - 'Zacute': (0x01ac, u'\u0179'), - 'Zcaron': (0x01ae, u'\u017D'), - 'Zstroke': (0x10001b5, u'\u01B5'), - 'a': (0x0061, u'\u0061'), - 'aacute': (0x00e1, u'\u00E1'), - 'abelowdot': (0x1001ea1, u'\u1EA1'), - 'abovedot': (0x01ff, u'\u02D9'), - 'abreve': (0x01e3, u'\u0103'), - 'abreveacute': (0x1001eaf, u'\u1EAF'), - 'abrevebelowdot': (0x1001eb7, u'\u1EB7'), - 'abrevegrave': (0x1001eb1, u'\u1EB1'), - 'abrevehook': (0x1001eb3, u'\u1EB3'), - 'abrevetilde': (0x1001eb5, u'\u1EB5'), - 'acircumflex': (0x00e2, u'\u00E2'), - 'acircumflexacute': (0x1001ea5, u'\u1EA5'), - 'acircumflexbelowdot': (0x1001ead, u'\u1EAD'), - 'acircumflexgrave': (0x1001ea7, u'\u1EA7'), - 'acircumflexhook': (0x1001ea9, u'\u1EA9'), - 'acircumflextilde': (0x1001eab, u'\u1EAB'), - 'acute': (0x00b4, u'\u00B4'), - 'adiaeresis': (0x00e4, u'\u00E4'), - 'ae': (0x00e6, u'\u00E6'), - 'agrave': (0x00e0, u'\u00E0'), - 'ahook': (0x1001ea3, u'\u1EA3'), - 'amacron': (0x03e0, u'\u0101'), - 'ampersand': (0x0026, u'\u0026'), - 'aogonek': (0x01b1, u'\u0105'), - 'apostrophe': (0x0027, u'\u0027'), - 'approxeq': (0x1002248, u'\u2245'), - 'approximate': (0x08c8, u'\u223C'), - 'aring': (0x00e5, u'\u00E5'), - 'asciicircum': (0x005e, u'\u005E'), - 'asciitilde': (0x007e, u'\u007E'), - 'asterisk': (0x002a, u'\u002A'), - 'at': (0x0040, u'\u0040'), - 'atilde': (0x00e3, u'\u00E3'), - 'b': (0x0062, u'\u0062'), - 'babovedot': (0x1001e03, u'\u1E03'), - 'backslash': (0x005c, u'\u005C'), - 'ballotcross': (0x0af4, u'\u2717'), - 'bar': (0x007c, u'\u007C'), - 'because': (0x1002235, u'\u2235'), - 'botintegral': (0x08a5, u'\u2321'), - 'botleftparens': (0x08ac, u'\u239D'), - 'botleftsqbracket': (0x08a8, u'\u23A3'), - 'botrightparens': (0x08ae, u'\u23A0'), - 'botrightsqbracket': (0x08aa, u'\u23A6'), - 'bott': (0x09f6, u'\u2534'), - 'braceleft': (0x007b, u'\u007B'), - 'braceright': (0x007d, u'\u007D'), - 'bracketleft': (0x005b, u'\u005B'), - 'bracketright': (0x005d, u'\u005D'), - 'braille_blank': (0x1002800, u'\u2800'), - 'braille_dots_1': (0x1002801, u'\u2801'), - 'braille_dots_12': (0x1002803, u'\u2803'), - 'braille_dots_123': (0x1002807, u'\u2807'), - 'braille_dots_1234': (0x100280f, u'\u280f'), - 'braille_dots_12345': (0x100281f, u'\u281f'), - 'braille_dots_123456': (0x100283f, u'\u283f'), - 'braille_dots_1234567': (0x100287f, u'\u287f'), - 'braille_dots_12345678': (0x10028ff, u'\u28ff'), - 'braille_dots_1234568': (0x10028bf, u'\u28bf'), - 'braille_dots_123457': (0x100285f, u'\u285f'), - 'braille_dots_1234578': (0x10028df, u'\u28df'), - 'braille_dots_123458': (0x100289f, u'\u289f'), - 'braille_dots_12346': (0x100282f, u'\u282f'), - 'braille_dots_123467': (0x100286f, u'\u286f'), - 'braille_dots_1234678': (0x10028ef, u'\u28ef'), - 'braille_dots_123468': (0x10028af, u'\u28af'), - 'braille_dots_12347': (0x100284f, u'\u284f'), - 'braille_dots_123478': (0x10028cf, u'\u28cf'), - 'braille_dots_12348': (0x100288f, u'\u288f'), - 'braille_dots_1235': (0x1002817, u'\u2817'), - 'braille_dots_12356': (0x1002837, u'\u2837'), - 'braille_dots_123567': (0x1002877, u'\u2877'), - 'braille_dots_1235678': (0x10028f7, u'\u28f7'), - 'braille_dots_123568': (0x10028b7, u'\u28b7'), - 'braille_dots_12357': (0x1002857, u'\u2857'), - 'braille_dots_123578': (0x10028d7, u'\u28d7'), - 'braille_dots_12358': (0x1002897, u'\u2897'), - 'braille_dots_1236': (0x1002827, u'\u2827'), - 'braille_dots_12367': (0x1002867, u'\u2867'), - 'braille_dots_123678': (0x10028e7, u'\u28e7'), - 'braille_dots_12368': (0x10028a7, u'\u28a7'), - 'braille_dots_1237': (0x1002847, u'\u2847'), - 'braille_dots_12378': (0x10028c7, u'\u28c7'), - 'braille_dots_1238': (0x1002887, u'\u2887'), - 'braille_dots_124': (0x100280b, u'\u280b'), - 'braille_dots_1245': (0x100281b, u'\u281b'), - 'braille_dots_12456': (0x100283b, u'\u283b'), - 'braille_dots_124567': (0x100287b, u'\u287b'), - 'braille_dots_1245678': (0x10028fb, u'\u28fb'), - 'braille_dots_124568': (0x10028bb, u'\u28bb'), - 'braille_dots_12457': (0x100285b, u'\u285b'), - 'braille_dots_124578': (0x10028db, u'\u28db'), - 'braille_dots_12458': (0x100289b, u'\u289b'), - 'braille_dots_1246': (0x100282b, u'\u282b'), - 'braille_dots_12467': (0x100286b, u'\u286b'), - 'braille_dots_124678': (0x10028eb, u'\u28eb'), - 'braille_dots_12468': (0x10028ab, u'\u28ab'), - 'braille_dots_1247': (0x100284b, u'\u284b'), - 'braille_dots_12478': (0x10028cb, u'\u28cb'), - 'braille_dots_1248': (0x100288b, u'\u288b'), - 'braille_dots_125': (0x1002813, u'\u2813'), - 'braille_dots_1256': (0x1002833, u'\u2833'), - 'braille_dots_12567': (0x1002873, u'\u2873'), - 'braille_dots_125678': (0x10028f3, u'\u28f3'), - 'braille_dots_12568': (0x10028b3, u'\u28b3'), - 'braille_dots_1257': (0x1002853, u'\u2853'), - 'braille_dots_12578': (0x10028d3, u'\u28d3'), - 'braille_dots_1258': (0x1002893, u'\u2893'), - 'braille_dots_126': (0x1002823, u'\u2823'), - 'braille_dots_1267': (0x1002863, u'\u2863'), - 'braille_dots_12678': (0x10028e3, u'\u28e3'), - 'braille_dots_1268': (0x10028a3, u'\u28a3'), - 'braille_dots_127': (0x1002843, u'\u2843'), - 'braille_dots_1278': (0x10028c3, u'\u28c3'), - 'braille_dots_128': (0x1002883, u'\u2883'), - 'braille_dots_13': (0x1002805, u'\u2805'), - 'braille_dots_134': (0x100280d, u'\u280d'), - 'braille_dots_1345': (0x100281d, u'\u281d'), - 'braille_dots_13456': (0x100283d, u'\u283d'), - 'braille_dots_134567': (0x100287d, u'\u287d'), - 'braille_dots_1345678': (0x10028fd, u'\u28fd'), - 'braille_dots_134568': (0x10028bd, u'\u28bd'), - 'braille_dots_13457': (0x100285d, u'\u285d'), - 'braille_dots_134578': (0x10028dd, u'\u28dd'), - 'braille_dots_13458': (0x100289d, u'\u289d'), - 'braille_dots_1346': (0x100282d, u'\u282d'), - 'braille_dots_13467': (0x100286d, u'\u286d'), - 'braille_dots_134678': (0x10028ed, u'\u28ed'), - 'braille_dots_13468': (0x10028ad, u'\u28ad'), - 'braille_dots_1347': (0x100284d, u'\u284d'), - 'braille_dots_13478': (0x10028cd, u'\u28cd'), - 'braille_dots_1348': (0x100288d, u'\u288d'), - 'braille_dots_135': (0x1002815, u'\u2815'), - 'braille_dots_1356': (0x1002835, u'\u2835'), - 'braille_dots_13567': (0x1002875, u'\u2875'), - 'braille_dots_135678': (0x10028f5, u'\u28f5'), - 'braille_dots_13568': (0x10028b5, u'\u28b5'), - 'braille_dots_1357': (0x1002855, u'\u2855'), - 'braille_dots_13578': (0x10028d5, u'\u28d5'), - 'braille_dots_1358': (0x1002895, u'\u2895'), - 'braille_dots_136': (0x1002825, u'\u2825'), - 'braille_dots_1367': (0x1002865, u'\u2865'), - 'braille_dots_13678': (0x10028e5, u'\u28e5'), - 'braille_dots_1368': (0x10028a5, u'\u28a5'), - 'braille_dots_137': (0x1002845, u'\u2845'), - 'braille_dots_1378': (0x10028c5, u'\u28c5'), - 'braille_dots_138': (0x1002885, u'\u2885'), - 'braille_dots_14': (0x1002809, u'\u2809'), - 'braille_dots_145': (0x1002819, u'\u2819'), - 'braille_dots_1456': (0x1002839, u'\u2839'), - 'braille_dots_14567': (0x1002879, u'\u2879'), - 'braille_dots_145678': (0x10028f9, u'\u28f9'), - 'braille_dots_14568': (0x10028b9, u'\u28b9'), - 'braille_dots_1457': (0x1002859, u'\u2859'), - 'braille_dots_14578': (0x10028d9, u'\u28d9'), - 'braille_dots_1458': (0x1002899, u'\u2899'), - 'braille_dots_146': (0x1002829, u'\u2829'), - 'braille_dots_1467': (0x1002869, u'\u2869'), - 'braille_dots_14678': (0x10028e9, u'\u28e9'), - 'braille_dots_1468': (0x10028a9, u'\u28a9'), - 'braille_dots_147': (0x1002849, u'\u2849'), - 'braille_dots_1478': (0x10028c9, u'\u28c9'), - 'braille_dots_148': (0x1002889, u'\u2889'), - 'braille_dots_15': (0x1002811, u'\u2811'), - 'braille_dots_156': (0x1002831, u'\u2831'), - 'braille_dots_1567': (0x1002871, u'\u2871'), - 'braille_dots_15678': (0x10028f1, u'\u28f1'), - 'braille_dots_1568': (0x10028b1, u'\u28b1'), - 'braille_dots_157': (0x1002851, u'\u2851'), - 'braille_dots_1578': (0x10028d1, u'\u28d1'), - 'braille_dots_158': (0x1002891, u'\u2891'), - 'braille_dots_16': (0x1002821, u'\u2821'), - 'braille_dots_167': (0x1002861, u'\u2861'), - 'braille_dots_1678': (0x10028e1, u'\u28e1'), - 'braille_dots_168': (0x10028a1, u'\u28a1'), - 'braille_dots_17': (0x1002841, u'\u2841'), - 'braille_dots_178': (0x10028c1, u'\u28c1'), - 'braille_dots_18': (0x1002881, u'\u2881'), - 'braille_dots_2': (0x1002802, u'\u2802'), - 'braille_dots_23': (0x1002806, u'\u2806'), - 'braille_dots_234': (0x100280e, u'\u280e'), - 'braille_dots_2345': (0x100281e, u'\u281e'), - 'braille_dots_23456': (0x100283e, u'\u283e'), - 'braille_dots_234567': (0x100287e, u'\u287e'), - 'braille_dots_2345678': (0x10028fe, u'\u28fe'), - 'braille_dots_234568': (0x10028be, u'\u28be'), - 'braille_dots_23457': (0x100285e, u'\u285e'), - 'braille_dots_234578': (0x10028de, u'\u28de'), - 'braille_dots_23458': (0x100289e, u'\u289e'), - 'braille_dots_2346': (0x100282e, u'\u282e'), - 'braille_dots_23467': (0x100286e, u'\u286e'), - 'braille_dots_234678': (0x10028ee, u'\u28ee'), - 'braille_dots_23468': (0x10028ae, u'\u28ae'), - 'braille_dots_2347': (0x100284e, u'\u284e'), - 'braille_dots_23478': (0x10028ce, u'\u28ce'), - 'braille_dots_2348': (0x100288e, u'\u288e'), - 'braille_dots_235': (0x1002816, u'\u2816'), - 'braille_dots_2356': (0x1002836, u'\u2836'), - 'braille_dots_23567': (0x1002876, u'\u2876'), - 'braille_dots_235678': (0x10028f6, u'\u28f6'), - 'braille_dots_23568': (0x10028b6, u'\u28b6'), - 'braille_dots_2357': (0x1002856, u'\u2856'), - 'braille_dots_23578': (0x10028d6, u'\u28d6'), - 'braille_dots_2358': (0x1002896, u'\u2896'), - 'braille_dots_236': (0x1002826, u'\u2826'), - 'braille_dots_2367': (0x1002866, u'\u2866'), - 'braille_dots_23678': (0x10028e6, u'\u28e6'), - 'braille_dots_2368': (0x10028a6, u'\u28a6'), - 'braille_dots_237': (0x1002846, u'\u2846'), - 'braille_dots_2378': (0x10028c6, u'\u28c6'), - 'braille_dots_238': (0x1002886, u'\u2886'), - 'braille_dots_24': (0x100280a, u'\u280a'), - 'braille_dots_245': (0x100281a, u'\u281a'), - 'braille_dots_2456': (0x100283a, u'\u283a'), - 'braille_dots_24567': (0x100287a, u'\u287a'), - 'braille_dots_245678': (0x10028fa, u'\u28fa'), - 'braille_dots_24568': (0x10028ba, u'\u28ba'), - 'braille_dots_2457': (0x100285a, u'\u285a'), - 'braille_dots_24578': (0x10028da, u'\u28da'), - 'braille_dots_2458': (0x100289a, u'\u289a'), - 'braille_dots_246': (0x100282a, u'\u282a'), - 'braille_dots_2467': (0x100286a, u'\u286a'), - 'braille_dots_24678': (0x10028ea, u'\u28ea'), - 'braille_dots_2468': (0x10028aa, u'\u28aa'), - 'braille_dots_247': (0x100284a, u'\u284a'), - 'braille_dots_2478': (0x10028ca, u'\u28ca'), - 'braille_dots_248': (0x100288a, u'\u288a'), - 'braille_dots_25': (0x1002812, u'\u2812'), - 'braille_dots_256': (0x1002832, u'\u2832'), - 'braille_dots_2567': (0x1002872, u'\u2872'), - 'braille_dots_25678': (0x10028f2, u'\u28f2'), - 'braille_dots_2568': (0x10028b2, u'\u28b2'), - 'braille_dots_257': (0x1002852, u'\u2852'), - 'braille_dots_2578': (0x10028d2, u'\u28d2'), - 'braille_dots_258': (0x1002892, u'\u2892'), - 'braille_dots_26': (0x1002822, u'\u2822'), - 'braille_dots_267': (0x1002862, u'\u2862'), - 'braille_dots_2678': (0x10028e2, u'\u28e2'), - 'braille_dots_268': (0x10028a2, u'\u28a2'), - 'braille_dots_27': (0x1002842, u'\u2842'), - 'braille_dots_278': (0x10028c2, u'\u28c2'), - 'braille_dots_28': (0x1002882, u'\u2882'), - 'braille_dots_3': (0x1002804, u'\u2804'), - 'braille_dots_34': (0x100280c, u'\u280c'), - 'braille_dots_345': (0x100281c, u'\u281c'), - 'braille_dots_3456': (0x100283c, u'\u283c'), - 'braille_dots_34567': (0x100287c, u'\u287c'), - 'braille_dots_345678': (0x10028fc, u'\u28fc'), - 'braille_dots_34568': (0x10028bc, u'\u28bc'), - 'braille_dots_3457': (0x100285c, u'\u285c'), - 'braille_dots_34578': (0x10028dc, u'\u28dc'), - 'braille_dots_3458': (0x100289c, u'\u289c'), - 'braille_dots_346': (0x100282c, u'\u282c'), - 'braille_dots_3467': (0x100286c, u'\u286c'), - 'braille_dots_34678': (0x10028ec, u'\u28ec'), - 'braille_dots_3468': (0x10028ac, u'\u28ac'), - 'braille_dots_347': (0x100284c, u'\u284c'), - 'braille_dots_3478': (0x10028cc, u'\u28cc'), - 'braille_dots_348': (0x100288c, u'\u288c'), - 'braille_dots_35': (0x1002814, u'\u2814'), - 'braille_dots_356': (0x1002834, u'\u2834'), - 'braille_dots_3567': (0x1002874, u'\u2874'), - 'braille_dots_35678': (0x10028f4, u'\u28f4'), - 'braille_dots_3568': (0x10028b4, u'\u28b4'), - 'braille_dots_357': (0x1002854, u'\u2854'), - 'braille_dots_3578': (0x10028d4, u'\u28d4'), - 'braille_dots_358': (0x1002894, u'\u2894'), - 'braille_dots_36': (0x1002824, u'\u2824'), - 'braille_dots_367': (0x1002864, u'\u2864'), - 'braille_dots_3678': (0x10028e4, u'\u28e4'), - 'braille_dots_368': (0x10028a4, u'\u28a4'), - 'braille_dots_37': (0x1002844, u'\u2844'), - 'braille_dots_378': (0x10028c4, u'\u28c4'), - 'braille_dots_38': (0x1002884, u'\u2884'), - 'braille_dots_4': (0x1002808, u'\u2808'), - 'braille_dots_45': (0x1002818, u'\u2818'), - 'braille_dots_456': (0x1002838, u'\u2838'), - 'braille_dots_4567': (0x1002878, u'\u2878'), - 'braille_dots_45678': (0x10028f8, u'\u28f8'), - 'braille_dots_4568': (0x10028b8, u'\u28b8'), - 'braille_dots_457': (0x1002858, u'\u2858'), - 'braille_dots_4578': (0x10028d8, u'\u28d8'), - 'braille_dots_458': (0x1002898, u'\u2898'), - 'braille_dots_46': (0x1002828, u'\u2828'), - 'braille_dots_467': (0x1002868, u'\u2868'), - 'braille_dots_4678': (0x10028e8, u'\u28e8'), - 'braille_dots_468': (0x10028a8, u'\u28a8'), - 'braille_dots_47': (0x1002848, u'\u2848'), - 'braille_dots_478': (0x10028c8, u'\u28c8'), - 'braille_dots_48': (0x1002888, u'\u2888'), - 'braille_dots_5': (0x1002810, u'\u2810'), - 'braille_dots_56': (0x1002830, u'\u2830'), - 'braille_dots_567': (0x1002870, u'\u2870'), - 'braille_dots_5678': (0x10028f0, u'\u28f0'), - 'braille_dots_568': (0x10028b0, u'\u28b0'), - 'braille_dots_57': (0x1002850, u'\u2850'), - 'braille_dots_578': (0x10028d0, u'\u28d0'), - 'braille_dots_58': (0x1002890, u'\u2890'), - 'braille_dots_6': (0x1002820, u'\u2820'), - 'braille_dots_67': (0x1002860, u'\u2860'), - 'braille_dots_678': (0x10028e0, u'\u28e0'), - 'braille_dots_68': (0x10028a0, u'\u28a0'), - 'braille_dots_7': (0x1002840, u'\u2840'), - 'braille_dots_78': (0x10028c0, u'\u28c0'), - 'braille_dots_8': (0x1002880, u'\u2880'), - 'breve': (0x01a2, u'\u02D8'), - 'brokenbar': (0x00a6, u'\u00A6'), - 'c': (0x0063, u'\u0063'), - 'cabovedot': (0x02e5, u'\u010B'), - 'cacute': (0x01e6, u'\u0107'), - 'careof': (0x0ab8, u'\u2105'), - 'caret': (0x0afc, u'\u2038'), - 'caron': (0x01b7, u'\u02C7'), - 'ccaron': (0x01e8, u'\u010D'), - 'ccedilla': (0x00e7, u'\u00E7'), - 'ccircumflex': (0x02e6, u'\u0109'), - 'cedilla': (0x00b8, u'\u00B8'), - 'cent': (0x00a2, u'\u00A2'), - 'checkerboard': (0x09e1, u'\u2592'), - 'checkmark': (0x0af3, u'\u2713'), - 'circle': (0x0bcf, u'\u25CB'), - 'club': (0x0aec, u'\u2663'), - 'colon': (0x003a, u'\u003A'), - 'comma': (0x002c, u'\u002C'), - 'containsas': (0x100220B, u'\u220B'), - 'copyright': (0x00a9, u'\u00A9'), - 'cr': (0x09e4, u'\u240D'), - 'crossinglines': (0x09ee, u'\u253C'), - 'cuberoot': (0x100221B, u'\u221B'), - 'currency': (0x00a4, u'\u00A4'), - 'd': (0x0064, u'\u0064'), - 'dabovedot': (0x1001e0b, u'\u1E0B'), - 'dagger': (0x0af1, u'\u2020'), - 'dcaron': (0x01ef, u'\u010F'), - 'dead_A': (0xfe81, None), - 'dead_E': (0xfe83, None), - 'dead_I': (0xfe85, None), - 'dead_O': (0xfe87, None), - 'dead_U': (0xfe89, None), - 'dead_a': (0xfe80, None), - 'dead_abovecomma': (0xfe64, u'\u0315'), - 'dead_abovedot': (0xfe56, u'\u0307'), - 'dead_abovereversedcomma': (0xfe65, u'\u0312'), - 'dead_abovering': (0xfe58, u'\u030A'), - 'dead_aboveverticalline': (0xfe91, u'\u030D'), - 'dead_acute': (0xfe51, u'\u0301'), - 'dead_belowbreve': (0xfe6b, u'\u032E'), - 'dead_belowcircumflex': (0xfe69, u'\u032D'), - 'dead_belowcomma': (0xfe6e, u'\u0326'), - 'dead_belowdiaeresis': (0xfe6c, u'\u0324'), - 'dead_belowdot': (0xfe60, u'\u0323'), - 'dead_belowmacron': (0xfe68, u'\u0331'), - 'dead_belowring': (0xfe67, u'\u0325'), - 'dead_belowtilde': (0xfe6a, u'\u0330'), - 'dead_belowverticalline': (0xfe92, u'\u0329'), - 'dead_breve': (0xfe55, u'\u0306'), - 'dead_capital_schwa': (0xfe8b, None), - 'dead_caron': (0xfe5a, u'\u030C'), - 'dead_cedilla': (0xfe5b, u'\u0327'), - 'dead_circumflex': (0xfe52, u'\u0302'), - 'dead_currency': (0xfe6f, None), - 'dead_diaeresis': (0xfe57, u'\u0308'), - 'dead_doubleacute': (0xfe59, u'\u030B'), - 'dead_doublegrave': (0xfe66, u'\u030F'), - 'dead_e': (0xfe82, None), - 'dead_grave': (0xfe50, u'\u0300'), - 'dead_greek': (0xfe8c, None), - 'dead_hook': (0xfe61, u'\u0309'), - 'dead_horn': (0xfe62, u'\u031B'), - 'dead_i': (0xfe84, None), - 'dead_invertedbreve': (0xfe6d, u'\u032F'), - 'dead_iota': (0xfe5d, u'\u0345'), - 'dead_longsolidusoverlay': (0xfe93, u'\u0338'), - 'dead_lowline': (0xfe90, u'\u0332'), - 'dead_macron': (0xfe54, u'\u0304'), - 'dead_o': (0xfe86, None), - 'dead_ogonek': (0xfe5c, u'\u0328'), - 'dead_semivoiced_sound': (0xfe5f, None), - 'dead_small_schwa': (0xfe8a, None), - 'dead_stroke': (0xfe63, u'\u0335'), - 'dead_tilde': (0xfe53, u'\u0303'), - 'dead_u': (0xfe88, None), - 'dead_voiced_sound': (0xfe5e, None), - 'degree': (0x00b0, u'\u00B0'), - 'diaeresis': (0x00a8, u'\u00A8'), - 'diamond': (0x0aed, u'\u2666'), - 'digitspace': (0x0aa5, u'\u2007'), - 'dintegral': (0x100222C, u'\u222C'), - 'division': (0x00f7, u'\u00F7'), - 'dollar': (0x0024, u'\u0024'), - 'doubbaselinedot': (0x0aaf, u'\u2025'), - 'doubleacute': (0x01bd, u'\u02DD'), - 'doubledagger': (0x0af2, u'\u2021'), - 'doublelowquotemark': (0x0afe, u'\u201E'), - 'downarrow': (0x08fe, u'\u2193'), - 'downstile': (0x0bc4, u'\u230A'), - 'downtack': (0x0bc2, u'\u22A4'), - 'dstroke': (0x01f0, u'\u0111'), - 'e': (0x0065, u'\u0065'), - 'eabovedot': (0x03ec, u'\u0117'), - 'eacute': (0x00e9, u'\u00E9'), - 'ebelowdot': (0x1001eb9, u'\u1EB9'), - 'ecaron': (0x01ec, u'\u011B'), - 'ecircumflex': (0x00ea, u'\u00EA'), - 'ecircumflexacute': (0x1001ebf, u'\u1EBF'), - 'ecircumflexbelowdot': (0x1001ec7, u'\u1EC7'), - 'ecircumflexgrave': (0x1001ec1, u'\u1EC1'), - 'ecircumflexhook': (0x1001ec3, u'\u1EC3'), - 'ecircumflextilde': (0x1001ec5, u'\u1EC5'), - 'ediaeresis': (0x00eb, u'\u00EB'), - 'egrave': (0x00e8, u'\u00E8'), - 'ehook': (0x1001ebb, u'\u1EBB'), - 'eightsubscript': (0x1002088, u'\u2088'), - 'eightsuperior': (0x1002078, u'\u2078'), - 'elementof': (0x1002208, u'\u2208'), - 'ellipsis': (0x0aae, u'\u2026'), - 'em3space': (0x0aa3, u'\u2004'), - 'em4space': (0x0aa4, u'\u2005'), - 'emacron': (0x03ba, u'\u0113'), - 'emdash': (0x0aa9, u'\u2014'), - 'emptyset': (0x1002205, u'\u2205'), - 'emspace': (0x0aa1, u'\u2003'), - 'endash': (0x0aaa, u'\u2013'), - 'eng': (0x03bf, u'\u014B'), - 'enspace': (0x0aa2, u'\u2002'), - 'eogonek': (0x01ea, u'\u0119'), - 'equal': (0x003d, u'\u003D'), - 'eth': (0x00f0, u'\u00F0'), - 'etilde': (0x1001ebd, u'\u1EBD'), - 'exclam': (0x0021, u'\u0021'), - 'exclamdown': (0x00a1, u'\u00A1'), - 'ezh': (0x1000292, u'\u0292'), - 'f': (0x0066, u'\u0066'), - 'fabovedot': (0x1001e1f, u'\u1E1F'), - 'femalesymbol': (0x0af8, u'\u2640'), - 'ff': (0x09e3, u'\u240C'), - 'figdash': (0x0abb, u'\u2012'), - 'fiveeighths': (0x0ac5, u'\u215D'), - 'fivesixths': (0x0ab7, u'\u215A'), - 'fivesubscript': (0x1002085, u'\u2085'), - 'fivesuperior': (0x1002075, u'\u2075'), - 'fourfifths': (0x0ab5, u'\u2158'), - 'foursubscript': (0x1002084, u'\u2084'), - 'foursuperior': (0x1002074, u'\u2074'), - 'fourthroot': (0x100221C, u'\u221C'), - 'function': (0x08f6, u'\u0192'), - 'g': (0x0067, u'\u0067'), - 'gabovedot': (0x02f5, u'\u0121'), - 'gbreve': (0x02bb, u'\u011F'), - 'gcaron': (0x10001e7, u'\u01E7'), - 'gcedilla': (0x03bb, u'\u0123'), - 'gcircumflex': (0x02f8, u'\u011D'), - 'grave': (0x0060, u'\u0060'), - 'greater': (0x003e, u'\u003E'), - 'greaterthanequal': (0x08be, u'\u2265'), - 'guillemotleft': (0x00ab, u'\u00AB'), - 'guillemotright': (0x00bb, u'\u00BB'), - 'h': (0x0068, u'\u0068'), - 'hairspace': (0x0aa8, u'\u200A'), - 'hcircumflex': (0x02b6, u'\u0125'), - 'heart': (0x0aee, u'\u2665'), - 'hebrew_aleph': (0x0ce0, u'\u05D0'), - 'hebrew_ayin': (0x0cf2, u'\u05E2'), - 'hebrew_bet': (0x0ce1, u'\u05D1'), - 'hebrew_chet': (0x0ce7, u'\u05D7'), - 'hebrew_dalet': (0x0ce3, u'\u05D3'), - 'hebrew_doublelowline': (0x0cdf, u'\u2017'), - 'hebrew_finalkaph': (0x0cea, u'\u05DA'), - 'hebrew_finalmem': (0x0ced, u'\u05DD'), - 'hebrew_finalnun': (0x0cef, u'\u05DF'), - 'hebrew_finalpe': (0x0cf3, u'\u05E3'), - 'hebrew_finalzade': (0x0cf5, u'\u05E5'), - 'hebrew_gimel': (0x0ce2, u'\u05D2'), - 'hebrew_he': (0x0ce4, u'\u05D4'), - 'hebrew_kaph': (0x0ceb, u'\u05DB'), - 'hebrew_lamed': (0x0cec, u'\u05DC'), - 'hebrew_mem': (0x0cee, u'\u05DE'), - 'hebrew_nun': (0x0cf0, u'\u05E0'), - 'hebrew_pe': (0x0cf4, u'\u05E4'), - 'hebrew_qoph': (0x0cf7, u'\u05E7'), - 'hebrew_resh': (0x0cf8, u'\u05E8'), - 'hebrew_samech': (0x0cf1, u'\u05E1'), - 'hebrew_shin': (0x0cf9, u'\u05E9'), - 'hebrew_taw': (0x0cfa, u'\u05EA'), - 'hebrew_tet': (0x0ce8, u'\u05D8'), - 'hebrew_waw': (0x0ce5, u'\u05D5'), - 'hebrew_yod': (0x0ce9, u'\u05D9'), - 'hebrew_zade': (0x0cf6, u'\u05E6'), - 'hebrew_zain': (0x0ce6, u'\u05D6'), - 'horizlinescan1': (0x09ef, u'\u23BA'), - 'horizlinescan3': (0x09f0, u'\u23BB'), - 'horizlinescan5': (0x09f1, u'\u2500'), - 'horizlinescan7': (0x09f2, u'\u23BC'), - 'horizlinescan9': (0x09f3, u'\u23BD'), - 'hstroke': (0x02b1, u'\u0127'), - 'ht': (0x09e2, u'\u2409'), - 'hyphen': (0x00ad, u'\u00AD'), - 'i': (0x0069, u'\u0069'), - 'iacute': (0x00ed, u'\u00ED'), - 'ibelowdot': (0x1001ecb, u'\u1ECB'), - 'ibreve': (0x100012d, u'\u012D'), - 'icircumflex': (0x00ee, u'\u00EE'), - 'identical': (0x08cf, u'\u2261'), - 'idiaeresis': (0x00ef, u'\u00EF'), - 'idotless': (0x02b9, u'\u0131'), - 'ifonlyif': (0x08cd, u'\u21D4'), - 'igrave': (0x00ec, u'\u00EC'), - 'ihook': (0x1001ec9, u'\u1EC9'), - 'imacron': (0x03ef, u'\u012B'), - 'implies': (0x08ce, u'\u21D2'), - 'includedin': (0x08da, u'\u2282'), - 'includes': (0x08db, u'\u2283'), - 'infinity': (0x08c2, u'\u221E'), - 'integral': (0x08bf, u'\u222B'), - 'intersection': (0x08dc, u'\u2229'), - 'iogonek': (0x03e7, u'\u012F'), - 'itilde': (0x03b5, u'\u0129'), - 'j': (0x006a, u'\u006A'), - 'jcircumflex': (0x02bc, u'\u0135'), - 'jot': (0x0bca, u'\u2218'), - 'k': (0x006b, u'\u006B'), - 'kana_A': (0x04b1, u'\u30A2'), - 'kana_CHI': (0x04c1, u'\u30C1'), - 'kana_E': (0x04b4, u'\u30A8'), - 'kana_FU': (0x04cc, u'\u30D5'), - 'kana_HA': (0x04ca, u'\u30CF'), - 'kana_HE': (0x04cd, u'\u30D8'), - 'kana_HI': (0x04cb, u'\u30D2'), - 'kana_HO': (0x04ce, u'\u30DB'), - 'kana_I': (0x04b2, u'\u30A4'), - 'kana_KA': (0x04b6, u'\u30AB'), - 'kana_KE': (0x04b9, u'\u30B1'), - 'kana_KI': (0x04b7, u'\u30AD'), - 'kana_KO': (0x04ba, u'\u30B3'), - 'kana_KU': (0x04b8, u'\u30AF'), - 'kana_MA': (0x04cf, u'\u30DE'), - 'kana_ME': (0x04d2, u'\u30E1'), - 'kana_MI': (0x04d0, u'\u30DF'), - 'kana_MO': (0x04d3, u'\u30E2'), - 'kana_MU': (0x04d1, u'\u30E0'), - 'kana_N': (0x04dd, u'\u30F3'), - 'kana_NA': (0x04c5, u'\u30CA'), - 'kana_NE': (0x04c8, u'\u30CD'), - 'kana_NI': (0x04c6, u'\u30CB'), - 'kana_NO': (0x04c9, u'\u30CE'), - 'kana_NU': (0x04c7, u'\u30CC'), - 'kana_O': (0x04b5, u'\u30AA'), - 'kana_RA': (0x04d7, u'\u30E9'), - 'kana_RE': (0x04da, u'\u30EC'), - 'kana_RI': (0x04d8, u'\u30EA'), - 'kana_RO': (0x04db, u'\u30ED'), - 'kana_RU': (0x04d9, u'\u30EB'), - 'kana_SA': (0x04bb, u'\u30B5'), - 'kana_SE': (0x04be, u'\u30BB'), - 'kana_SHI': (0x04bc, u'\u30B7'), - 'kana_SO': (0x04bf, u'\u30BD'), - 'kana_SU': (0x04bd, u'\u30B9'), - 'kana_TA': (0x04c0, u'\u30BF'), - 'kana_TE': (0x04c3, u'\u30C6'), - 'kana_TO': (0x04c4, u'\u30C8'), - 'kana_TSU': (0x04c2, u'\u30C4'), - 'kana_U': (0x04b3, u'\u30A6'), - 'kana_WA': (0x04dc, u'\u30EF'), - 'kana_WO': (0x04a6, u'\u30F2'), - 'kana_YA': (0x04d4, u'\u30E4'), - 'kana_YO': (0x04d6, u'\u30E8'), - 'kana_YU': (0x04d5, u'\u30E6'), - 'kana_a': (0x04a7, u'\u30A1'), - 'kana_closingbracket': (0x04a3, u'\u300D'), - 'kana_comma': (0x04a4, u'\u3001'), - 'kana_conjunctive': (0x04a5, u'\u30FB'), - 'kana_e': (0x04aa, u'\u30A7'), - 'kana_fullstop': (0x04a1, u'\u3002'), - 'kana_i': (0x04a8, u'\u30A3'), - 'kana_o': (0x04ab, u'\u30A9'), - 'kana_openingbracket': (0x04a2, u'\u300C'), - 'kana_tsu': (0x04af, u'\u30C3'), - 'kana_u': (0x04a9, u'\u30A5'), - 'kana_ya': (0x04ac, u'\u30E3'), - 'kana_yo': (0x04ae, u'\u30E7'), - 'kana_yu': (0x04ad, u'\u30E5'), - 'kcedilla': (0x03f3, u'\u0137'), - 'kra': (0x03a2, u'\u0138'), - 'l': (0x006c, u'\u006C'), - 'lacute': (0x01e5, u'\u013A'), - 'latincross': (0x0ad9, u'\u271D'), - 'lbelowdot': (0x1001e37, u'\u1E37'), - 'lcaron': (0x01b5, u'\u013E'), - 'lcedilla': (0x03b6, u'\u013C'), - 'leftarrow': (0x08fb, u'\u2190'), - 'leftdoublequotemark': (0x0ad2, u'\u201C'), - 'leftmiddlecurlybrace': (0x08af, u'\u23A8'), - 'leftradical': (0x08a1, u'\u23B7'), - 'leftsinglequotemark': (0x0ad0, u'\u2018'), - 'leftt': (0x09f4, u'\u251C'), - 'lefttack': (0x0bdc, u'\u22A3'), - 'less': (0x003c, u'\u003C'), - 'lessthanequal': (0x08bc, u'\u2264'), - 'lf': (0x09e5, u'\u240A'), - 'logicaland': (0x08de, u'\u2227'), - 'logicalor': (0x08df, u'\u2228'), - 'lowleftcorner': (0x09ed, u'\u2514'), - 'lowrightcorner': (0x09ea, u'\u2518'), - 'lstroke': (0x01b3, u'\u0142'), - 'm': (0x006d, u'\u006D'), - 'mabovedot': (0x1001e41, u'\u1E41'), - 'macron': (0x00af, u'\u00AF'), - 'malesymbol': (0x0af7, u'\u2642'), - 'maltesecross': (0x0af0, u'\u2720'), - 'masculine': (0x00ba, u'\u00BA'), - 'minus': (0x002d, u'\u002D'), - 'minutes': (0x0ad6, u'\u2032'), - 'mu': (0x00b5, u'\u00B5'), - 'multiply': (0x00d7, u'\u00D7'), - 'musicalflat': (0x0af6, u'\u266D'), - 'musicalsharp': (0x0af5, u'\u266F'), - 'n': (0x006e, u'\u006E'), - 'nabla': (0x08c5, u'\u2207'), - 'nacute': (0x01f1, u'\u0144'), - 'ncaron': (0x01f2, u'\u0148'), - 'ncedilla': (0x03f1, u'\u0146'), - 'ninesubscript': (0x1002089, u'\u2089'), - 'ninesuperior': (0x1002079, u'\u2079'), - 'nl': (0x09e8, u'\u2424'), - 'nobreakspace': (0x00a0, u'\u00A0'), - 'notapproxeq': (0x1002247, u'\u2247'), - 'notelementof': (0x1002209, u'\u2209'), - 'notequal': (0x08bd, u'\u2260'), - 'notidentical': (0x1002262, u'\u2262'), - 'notsign': (0x00ac, u'\u00AC'), - 'ntilde': (0x00f1, u'\u00F1'), - 'numbersign': (0x0023, u'\u0023'), - 'numerosign': (0x06b0, u'\u2116'), - 'o': (0x006f, u'\u006F'), - 'oacute': (0x00f3, u'\u00F3'), - 'obarred': (0x1000275, u'\u0275'), - 'obelowdot': (0x1001ecd, u'\u1ECD'), - 'ocaron': (0x10001d2, u'\u01D2'), - 'ocircumflex': (0x00f4, u'\u00F4'), - 'ocircumflexacute': (0x1001ed1, u'\u1ED1'), - 'ocircumflexbelowdot': (0x1001ed9, u'\u1ED9'), - 'ocircumflexgrave': (0x1001ed3, u'\u1ED3'), - 'ocircumflexhook': (0x1001ed5, u'\u1ED5'), - 'ocircumflextilde': (0x1001ed7, u'\u1ED7'), - 'odiaeresis': (0x00f6, u'\u00F6'), - 'odoubleacute': (0x01f5, u'\u0151'), - 'oe': (0x13bd, u'\u0153'), - 'ogonek': (0x01b2, u'\u02DB'), - 'ograve': (0x00f2, u'\u00F2'), - 'ohook': (0x1001ecf, u'\u1ECF'), - 'ohorn': (0x10001a1, u'\u01A1'), - 'ohornacute': (0x1001edb, u'\u1EDB'), - 'ohornbelowdot': (0x1001ee3, u'\u1EE3'), - 'ohorngrave': (0x1001edd, u'\u1EDD'), - 'ohornhook': (0x1001edf, u'\u1EDF'), - 'ohorntilde': (0x1001ee1, u'\u1EE1'), - 'omacron': (0x03f2, u'\u014D'), - 'oneeighth': (0x0ac3, u'\u215B'), - 'onefifth': (0x0ab2, u'\u2155'), - 'onehalf': (0x00bd, u'\u00BD'), - 'onequarter': (0x00bc, u'\u00BC'), - 'onesixth': (0x0ab6, u'\u2159'), - 'onesubscript': (0x1002081, u'\u2081'), - 'onesuperior': (0x00b9, u'\u00B9'), - 'onethird': (0x0ab0, u'\u2153'), - 'ooblique': (0x00f8, u'\u00F8'), - 'ordfeminine': (0x00aa, u'\u00AA'), - 'oslash': (0x00f8, u'\u00F8'), - 'otilde': (0x00f5, u'\u00F5'), - 'overline': (0x047e, u'\u203E'), - 'p': (0x0070, u'\u0070'), - 'pabovedot': (0x1001e57, u'\u1E57'), - 'paragraph': (0x00b6, u'\u00B6'), - 'parenleft': (0x0028, u'\u0028'), - 'parenright': (0x0029, u'\u0029'), - 'partdifferential': (0x1002202, u'\u2202'), - 'partialderivative': (0x08ef, u'\u2202'), - 'percent': (0x0025, u'\u0025'), - 'period': (0x002e, u'\u002E'), - 'periodcentered': (0x00b7, u'\u00B7'), - 'permille': (0x0ad5, u'\u2030'), - 'phonographcopyright': (0x0afb, u'\u2117'), - 'plus': (0x002b, u'\u002B'), - 'plusminus': (0x00b1, u'\u00B1'), - 'prescription': (0x0ad4, u'\u211E'), - 'prolongedsound': (0x04b0, u'\u30FC'), - 'punctspace': (0x0aa6, u'\u2008'), - 'q': (0x0071, u'\u0071'), - 'quad': (0x0bcc, u'\u2395'), - 'question': (0x003f, u'\u003F'), - 'questiondown': (0x00bf, u'\u00BF'), - 'quotedbl': (0x0022, u'\u0022'), - 'r': (0x0072, u'\u0072'), - 'racute': (0x01e0, u'\u0155'), - 'radical': (0x08d6, u'\u221A'), - 'rcaron': (0x01f8, u'\u0159'), - 'rcedilla': (0x03b3, u'\u0157'), - 'registered': (0x00ae, u'\u00AE'), - 'rightarrow': (0x08fd, u'\u2192'), - 'rightdoublequotemark': (0x0ad3, u'\u201D'), - 'rightmiddlecurlybrace': (0x08b0, u'\u23AC'), - 'rightsinglequotemark': (0x0ad1, u'\u2019'), - 'rightt': (0x09f5, u'\u2524'), - 'righttack': (0x0bfc, u'\u22A2'), - 's': (0x0073, u'\u0073'), - 'sabovedot': (0x1001e61, u'\u1E61'), - 'sacute': (0x01b6, u'\u015B'), - 'scaron': (0x01b9, u'\u0161'), - 'scedilla': (0x01ba, u'\u015F'), - 'schwa': (0x1000259, u'\u0259'), - 'scircumflex': (0x02fe, u'\u015D'), - 'seconds': (0x0ad7, u'\u2033'), - 'section': (0x00a7, u'\u00A7'), - 'semicolon': (0x003b, u'\u003B'), - 'semivoicedsound': (0x04df, u'\u309C'), - 'seveneighths': (0x0ac6, u'\u215E'), - 'sevensubscript': (0x1002087, u'\u2087'), - 'sevensuperior': (0x1002077, u'\u2077'), - 'similarequal': (0x08c9, u'\u2243'), - 'singlelowquotemark': (0x0afd, u'\u201A'), - 'sixsubscript': (0x1002086, u'\u2086'), - 'sixsuperior': (0x1002076, u'\u2076'), - 'slash': (0x002f, u'\u002F'), - 'soliddiamond': (0x09e0, u'\u25C6'), - 'space': (0x0020, u'\u0020'), - 'squareroot': (0x100221A, u'\u221A'), - 'ssharp': (0x00df, u'\u00DF'), - 'sterling': (0x00a3, u'\u00A3'), - 'stricteq': (0x1002263, u'\u2263'), - 't': (0x0074, u'\u0074'), - 'tabovedot': (0x1001e6b, u'\u1E6B'), - 'tcaron': (0x01bb, u'\u0165'), - 'tcedilla': (0x01fe, u'\u0163'), - 'telephone': (0x0af9, u'\u260E'), - 'telephonerecorder': (0x0afa, u'\u2315'), - 'therefore': (0x08c0, u'\u2234'), - 'thinspace': (0x0aa7, u'\u2009'), - 'thorn': (0x00fe, u'\u00FE'), - 'threeeighths': (0x0ac4, u'\u215C'), - 'threefifths': (0x0ab4, u'\u2157'), - 'threequarters': (0x00be, u'\u00BE'), - 'threesubscript': (0x1002083, u'\u2083'), - 'threesuperior': (0x00b3, u'\u00B3'), - 'tintegral': (0x100222D, u'\u222D'), - 'topintegral': (0x08a4, u'\u2320'), - 'topleftparens': (0x08ab, u'\u239B'), - 'topleftsqbracket': (0x08a7, u'\u23A1'), - 'toprightparens': (0x08ad, u'\u239E'), - 'toprightsqbracket': (0x08a9, u'\u23A4'), - 'topt': (0x09f7, u'\u252C'), - 'trademark': (0x0ac9, u'\u2122'), - 'tslash': (0x03bc, u'\u0167'), - 'twofifths': (0x0ab3, u'\u2156'), - 'twosubscript': (0x1002082, u'\u2082'), - 'twosuperior': (0x00b2, u'\u00B2'), - 'twothirds': (0x0ab1, u'\u2154'), - 'u': (0x0075, u'\u0075'), - 'uacute': (0x00fa, u'\u00FA'), - 'ubelowdot': (0x1001ee5, u'\u1EE5'), - 'ubreve': (0x02fd, u'\u016D'), - 'ucircumflex': (0x00fb, u'\u00FB'), - 'udiaeresis': (0x00fc, u'\u00FC'), - 'udoubleacute': (0x01fb, u'\u0171'), - 'ugrave': (0x00f9, u'\u00F9'), - 'uhook': (0x1001ee7, u'\u1EE7'), - 'uhorn': (0x10001b0, u'\u01B0'), - 'uhornacute': (0x1001ee9, u'\u1EE9'), - 'uhornbelowdot': (0x1001ef1, u'\u1EF1'), - 'uhorngrave': (0x1001eeb, u'\u1EEB'), - 'uhornhook': (0x1001eed, u'\u1EED'), - 'uhorntilde': (0x1001eef, u'\u1EEF'), - 'umacron': (0x03fe, u'\u016B'), - 'underscore': (0x005f, u'\u005F'), - 'union': (0x08dd, u'\u222A'), - 'uogonek': (0x03f9, u'\u0173'), - 'uparrow': (0x08fc, u'\u2191'), - 'upleftcorner': (0x09ec, u'\u250C'), - 'uprightcorner': (0x09eb, u'\u2510'), - 'upstile': (0x0bd3, u'\u2308'), - 'uptack': (0x0bce, u'\u22A5'), - 'uring': (0x01f9, u'\u016F'), - 'utilde': (0x03fd, u'\u0169'), - 'v': (0x0076, u'\u0076'), - 'variation': (0x08c1, u'\u221D'), - 'vertbar': (0x09f8, u'\u2502'), - 'voicedsound': (0x04de, u'\u309B'), - 'vt': (0x09e9, u'\u240B'), - 'w': (0x0077, u'\u0077'), - 'wacute': (0x1001e83, u'\u1E83'), - 'wcircumflex': (0x1000175, u'\u0175'), - 'wdiaeresis': (0x1001e85, u'\u1E85'), - 'wgrave': (0x1001e81, u'\u1E81'), - 'x': (0x0078, u'\u0078'), - 'xabovedot': (0x1001e8b, u'\u1E8B'), - 'y': (0x0079, u'\u0079'), - 'yacute': (0x00fd, u'\u00FD'), - 'ybelowdot': (0x1001ef5, u'\u1EF5'), - 'ycircumflex': (0x1000177, u'\u0177'), - 'ydiaeresis': (0x00ff, u'\u00FF'), - 'yen': (0x00a5, u'\u00A5'), - 'ygrave': (0x1001ef3, u'\u1EF3'), - 'yhook': (0x1001ef7, u'\u1EF7'), - 'ytilde': (0x1001ef9, u'\u1EF9'), - 'z': (0x007a, u'\u007A'), - 'zabovedot': (0x01bf, u'\u017C'), - 'zacute': (0x01bc, u'\u017A'), - 'zcaron': (0x01be, u'\u017E'), - 'zerosubscript': (0x1002080, u'\u2080'), - 'zerosuperior': (0x1002070, u'\u2070'), - 'zstroke': (0x10001b6, u'\u01B6')} - -DEAD_KEYS = { - u'\u0307': u'\u02D9', - u'\u030A': u'\u02DA', - u'\u0301': u'\u00B4', - u'\u0306': u'\u02D8', - u'\u030C': u'\u02C7', - u'\u0327': u'\u00B8', - u'\u0302': u'\u005E', - u'\u0308': u'\u00A8', - u'\u030B': u'\u02DD', - u'\u0300': u'\u0060', - u'\u0345': u'\u037A', - u'\u0332': u'\u005F', - u'\u0304': u'\u00AF', - u'\u0328': u'\u02DB', - u'\u0303': u'\u007E'} - -KEYPAD_KEYS = { - 'KP_0': 0xffb0, - 'KP_1': 0xffb1, - 'KP_2': 0xffb2, - 'KP_3': 0xffb3, - 'KP_4': 0xffb4, - 'KP_5': 0xffb5, - 'KP_6': 0xffb6, - 'KP_7': 0xffb7, - 'KP_8': 0xffb8, - 'KP_9': 0xffb9, - 'KP_Add': 0xffab, - 'KP_Begin': 0xff9d, - 'KP_Decimal': 0xffae, - 'KP_Delete': 0xff9f, - 'KP_Divide': 0xffaf, - 'KP_Down': 0xff99, - 'KP_End': 0xff9c, - 'KP_Enter': 0xff8d, - 'KP_Equal': 0xffbd, - 'KP_F1': 0xff91, - 'KP_F2': 0xff92, - 'KP_F3': 0xff93, - 'KP_F4': 0xff94, - 'KP_Home': 0xff95, - 'KP_Insert': 0xff9e, - 'KP_Left': 0xff96, - 'KP_Multiply': 0xffaa, - 'KP_Next': 0xff9b, - 'KP_Page_Down': 0xff9b, - 'KP_Page_Up': 0xff9a, - 'KP_Prior': 0xff9a, - 'KP_Right': 0xff98, - 'KP_Separator': 0xffac, - 'KP_Space': 0xff80, - 'KP_Subtract': 0xffad, - 'KP_Tab': 0xff89, - 'KP_Up': 0xff97} - -CHARS = { - codepoint: name - for name, (keysym, codepoint) in SYMBOLS.items() - if codepoint} - -KEYSYMS = { - keysym: name - for name, (keysym, codepoint) in SYMBOLS.items() - if codepoint} diff --git a/PortablePython/Lib/site-packages/pynput/keyboard/__init__.py b/PortablePython/Lib/site-packages/pynput/keyboard/__init__.py deleted file mode 100644 index 34a1921..0000000 --- a/PortablePython/Lib/site-packages/pynput/keyboard/__init__.py +++ /dev/null @@ -1,249 +0,0 @@ -# coding=utf-8 -# pynput -# Copyright (C) 2015-2024 Moses Palmér -# -# This program 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 3 of the License, or (at your option) any -# later version. -# -# This program 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 program. If not, see . -""" -The module containing keyboard classes. - -See the documentation for more information. -""" - -# pylint: disable=C0103 -# KeyCode, Key, Controller and Listener are not constants - -import itertools - -from pynput._util import backend, Events - - -backend = backend(__name__) -KeyCode = backend.KeyCode -Key = backend.Key -Controller = backend.Controller -Listener = backend.Listener -del backend - - -# pylint: disable=C0326; it is easier to read column aligned keys -#: The keys used as modifiers; the first value in each tuple is the -#: base modifier to use for subsequent modifiers. -_MODIFIER_KEYS = ( - (Key.alt_gr, (Key.alt_gr.value,)), - (Key.alt, (Key.alt.value, Key.alt_l.value, Key.alt_r.value)), - (Key.cmd, (Key.cmd.value, Key.cmd_l.value, Key.cmd_r.value)), - (Key.ctrl, (Key.ctrl.value, Key.ctrl_l.value, Key.ctrl_r.value)), - (Key.shift, (Key.shift.value, Key.shift_l.value, Key.shift_r.value))) - -#: Normalised modifiers as a mapping from virtual key code to basic modifier. -_NORMAL_MODIFIERS = { - value: key - for combination in _MODIFIER_KEYS - for key, value in zip( - itertools.cycle((combination[0],)), - combination[1])} - -#: Control codes to transform into key codes when typing -_CONTROL_CODES = { - '\n': Key.enter, - '\r': Key.enter, - '\t': Key.tab} -# pylint: enable=C0326 - - -class Events(Events): - """A keyboard event listener supporting synchronous iteration over the - events. - - Possible events are: - - :class:`Events.Press` - A key was pressed. - - :class:`Events.Release` - A key was released. - """ - _Listener = Listener - - class Press(Events.Event): - """A key press event. - """ - def __init__(self, key, injected): - #: The key. - self.key = key - - #: Whether this event is synthetic. - self.injected = injected - - class Release(Events.Event): - """A key release event. - """ - def __init__(self, key, injected): - #: The key. - self.key = key - - #: Whether this event is synthetic. - self.injected = injected - - def __init__(self): - super(Events, self).__init__( - on_press=self.Press, - on_release=self.Release) - - -class HotKey(object): - """A combination of keys acting as a hotkey. - - This class acts as a container of hotkey state for a keyboard listener. - - :param set keys: The collection of keys that must be pressed for this - hotkey to activate. Please note that a common limitation of the - hardware is that at most three simultaneously pressed keys are - supported, so using more keys may not work. - - :param callable on_activate: The activation callback. - """ - def __init__(self, keys, on_activate): - self._state = set() - self._keys = set(keys) - self._on_activate = on_activate - - @staticmethod - def parse(keys): - """Parses a key combination string. - - Key combination strings are sequences of key identifiers separated by - ``'+'``. Key identifiers are either single characters representing a - keyboard key, such as ``'a'``, or special key names identified by names - enclosed by brackets, such as ``''``. - - Keyboard keys are case-insensitive. - - :raises ValueError: if a part of the keys string is invalid, or if it - contains multiple equal parts - """ - def parts(): - start = 0 - for i, c in enumerate(keys): - if c == '+' and i != start: - yield keys[start:i] - start = i + 1 - if start == len(keys): - raise ValueError(keys) - else: - yield keys[start:] - - def parse(s): - if len(s) == 1: - return KeyCode.from_char(s.lower()) - elif len(s) > 2 and (s[0], s[-1]) == ('<', '>'): - p = s[1:-1] - try: - # We want to represent modifiers as Key instances, and all - # other keys as KeyCodes - key = Key[p.lower()] - if key in _NORMAL_MODIFIERS.values(): - return key - else: - return KeyCode.from_vk(key.value.vk) - except KeyError: - try: - return KeyCode.from_vk(int(p)) - except ValueError: - raise ValueError(s) - else: - raise ValueError(s) - - # Split the string and parse the individual parts - raw_parts = list(parts()) - parsed_parts = [ - parse(s) - for s in raw_parts] - - # Ensure no duplicate parts - if len(parsed_parts) != len(set(parsed_parts)): - raise ValueError(keys) - else: - return parsed_parts - - def press(self, key): - """Updates the hotkey state for a pressed key. - - If the key is not currently pressed, but is the last key for the full - combination, the activation callback will be invoked. - - Please note that the callback will only be invoked once. - - :param key: The key being pressed. - :type key: Key or KeyCode - """ - if key in self._keys and key not in self._state: - self._state.add(key) - if self._state == self._keys: - self._on_activate() - - def release(self, key): - """Updates the hotkey state for a released key. - - :param key: The key being released. - :type key: Key or KeyCode - """ - if key in self._state: - self._state.remove(key) - - -class GlobalHotKeys(Listener): - """A keyboard listener supporting a number of global hotkeys. - - This is a convenience wrapper to simplify registering a number of global - hotkeys. - - :param dict hotkeys: A mapping from hotkey description to hotkey action. - Keys are strings passed to :meth:`HotKey.parse`. - - :raises ValueError: if any hotkey description is invalid - """ - def __init__(self, hotkeys, *args, **kwargs): - self._hotkeys = [ - HotKey(HotKey.parse(key), value) - for key, value in hotkeys.items()] - super(GlobalHotKeys, self).__init__( - on_press=self._on_press, - on_release=self._on_release, - *args, - **kwargs) - - def _on_press(self, key, injected): - """The press callback. - - This is automatically registered upon creation. - - :param key: The key provided by the base class. - :param injected: Whether the event was injected. - """ - if not injected: - for hotkey in self._hotkeys: - hotkey.press(self.canonical(key)) - - def _on_release(self, key, injected): - """The release callback. - - This is automatically registered upon creation. - - :param key: The key provided by the base class. - :param injected: Whether the event was injected. - """ - if not injected: - for hotkey in self._hotkeys: - hotkey.release(self.canonical(key)) diff --git a/PortablePython/Lib/site-packages/pynput/keyboard/_base.py b/PortablePython/Lib/site-packages/pynput/keyboard/_base.py deleted file mode 100644 index 6035743..0000000 --- a/PortablePython/Lib/site-packages/pynput/keyboard/_base.py +++ /dev/null @@ -1,754 +0,0 @@ -# coding=utf-8 -# pynput -# Copyright (C) 2015-2024 Moses Palmér -# -# This program 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 3 of the License, or (at your option) any -# later version. -# -# This program 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 program. If not, see . -""" -This module contains the base implementation. - -The actual interface to keyboard classes is defined here, but the -implementation is located in a platform dependent module. -""" - -# pylint: disable=R0903 -# We implement stubs - -import contextlib -import enum -import threading -import unicodedata - -import six - -from pynput._util import AbstractListener, prefix -from pynput import _logger - - -class KeyCode(object): - """ - A :class:`KeyCode` represents the description of a key code used by the - operating system. - """ - #: The names of attributes used as platform extensions. - _PLATFORM_EXTENSIONS = [] - - def __init__(self, vk=None, char=None, is_dead=False, **kwargs): - self.vk = vk - self.char = six.text_type(char) if char is not None else None - self.is_dead = is_dead - - if self.is_dead: - try: - self.combining = unicodedata.lookup( - 'COMBINING ' + unicodedata.name(self.char)) - except KeyError: - self.is_dead = False - self.combining = None - if self.is_dead and not self.combining: - raise KeyError(char) - else: - self.combining = None - - for key in self._PLATFORM_EXTENSIONS: - setattr(self, key, kwargs.pop(key, None)) - if kwargs: - raise ValueError(kwargs) - - - def __repr__(self): - if self.is_dead: - return '[%s]' % repr(self.char) - if self.char is not None: - return repr(self.char) - else: - return '<%d>' % self.vk - - def __str__(self): - return repr(self) - - def __eq__(self, other): - if not isinstance(other, self.__class__): - return False - if self.char is not None and other.char is not None: - return self.char == other.char and self.is_dead == other.is_dead - else: - return self.vk == other.vk and all( - getattr(self, f) == getattr(other, f) - for f in self._PLATFORM_EXTENSIONS) - - def __hash__(self): - return hash(repr(self)) - - def join(self, key): - """Applies this dead key to another key and returns the result. - - Joining a dead key with space (``' '``) or itself yields the non-dead - version of this key, if one exists; for example, - ``KeyCode.from_dead('~').join(KeyCode.from_char(' '))`` equals - ``KeyCode.from_char('~')`` and - ``KeyCode.from_dead('~').join(KeyCode.from_dead('~'))``. - - :param KeyCode key: The key to join with this key. - - :return: a key code - - :raises ValueError: if the keys cannot be joined - """ - # A non-dead key cannot be joined - if not self.is_dead: - raise ValueError(self) - - # Joining two of the same keycodes, or joining with space, yields the - # non-dead version of the key - if key.char == ' ' or self == key: - return self.from_char(self.char) - - # Otherwise we combine the characters - if key.char is not None: - combined = unicodedata.normalize( - 'NFC', - key.char + self.combining) - if combined: - return self.from_char(combined) - - raise ValueError(key) - - @classmethod - def from_vk(cls, vk, **kwargs): - """Creates a key from a virtual key code. - - :param vk: The virtual key code. - - :param kwargs: Any other parameters to pass. - - :return: a key code - """ - return cls(vk=vk, **kwargs) - - @classmethod - def from_char(cls, char, **kwargs): - """Creates a key from a character. - - :param str char: The character. - - :return: a key code - """ - return cls(char=char, **kwargs) - - @classmethod - def from_dead(cls, char, **kwargs): - """Creates a dead key. - - :param char: The dead key. This should be the unicode character - representing the stand alone character, such as ``'~'`` for - *COMBINING TILDE*. - - :return: a key code - """ - return cls(char=char, is_dead=True, **kwargs) - - -class Key(enum.Enum): - """A class representing various buttons that may not correspond to - letters. This includes modifier keys and function keys. - - The actual values for these items differ between platforms. Some platforms - may have additional buttons, but these are guaranteed to be present - everywhere. - """ - #: A generic Alt key. This is a modifier. - alt = KeyCode.from_vk(0) - - #: The left Alt key. This is a modifier. - alt_l = KeyCode.from_vk(0) - - #: The right Alt key. This is a modifier. - alt_r = KeyCode.from_vk(0) - - #: The AltGr key. This is a modifier. - alt_gr = KeyCode.from_vk(0) - - #: The Backspace key. - backspace = KeyCode.from_vk(0) - - #: The CapsLock key. - caps_lock = KeyCode.from_vk(0) - - #: A generic command button. On *PC* platforms, this corresponds to the - #: Super key or Windows key, and on *Mac* it corresponds to the Command - #: key. This may be a modifier. - cmd = KeyCode.from_vk(0) - - #: The left command button. On *PC* platforms, this corresponds to the - #: Super key or Windows key, and on *Mac* it corresponds to the Command - #: key. This may be a modifier. - cmd_l = KeyCode.from_vk(0) - - #: The right command button. On *PC* platforms, this corresponds to the - #: Super key or Windows key, and on *Mac* it corresponds to the Command - #: key. This may be a modifier. - cmd_r = KeyCode.from_vk(0) - - #: A generic Ctrl key. This is a modifier. - ctrl = KeyCode.from_vk(0) - - #: The left Ctrl key. This is a modifier. - ctrl_l = KeyCode.from_vk(0) - - #: The right Ctrl key. This is a modifier. - ctrl_r = KeyCode.from_vk(0) - - #: The Delete key. - delete = KeyCode.from_vk(0) - - #: A down arrow key. - down = KeyCode.from_vk(0) - - #: The End key. - end = KeyCode.from_vk(0) - - #: The Enter or Return key. - enter = KeyCode.from_vk(0) - - #: The Esc key. - esc = KeyCode.from_vk(0) - - #: The function keys. F1 to F20 are defined. - f1 = KeyCode.from_vk(0) - f2 = KeyCode.from_vk(0) - f3 = KeyCode.from_vk(0) - f4 = KeyCode.from_vk(0) - f5 = KeyCode.from_vk(0) - f6 = KeyCode.from_vk(0) - f7 = KeyCode.from_vk(0) - f8 = KeyCode.from_vk(0) - f9 = KeyCode.from_vk(0) - f10 = KeyCode.from_vk(0) - f11 = KeyCode.from_vk(0) - f12 = KeyCode.from_vk(0) - f13 = KeyCode.from_vk(0) - f14 = KeyCode.from_vk(0) - f15 = KeyCode.from_vk(0) - f16 = KeyCode.from_vk(0) - f17 = KeyCode.from_vk(0) - f18 = KeyCode.from_vk(0) - f19 = KeyCode.from_vk(0) - f20 = KeyCode.from_vk(0) - - #: The Home key. - home = KeyCode.from_vk(0) - - #: A left arrow key. - left = KeyCode.from_vk(0) - - #: The PageDown key. - page_down = KeyCode.from_vk(0) - - #: The PageUp key. - page_up = KeyCode.from_vk(0) - - #: A right arrow key. - right = KeyCode.from_vk(0) - - #: A generic Shift key. This is a modifier. - shift = KeyCode.from_vk(0) - - #: The left Shift key. This is a modifier. - shift_l = KeyCode.from_vk(0) - - #: The right Shift key. This is a modifier. - shift_r = KeyCode.from_vk(0) - - #: The Space key. - space = KeyCode.from_vk(0) - - #: The Tab key. - tab = KeyCode.from_vk(0) - - #: An up arrow key. - up = KeyCode.from_vk(0) - - #: The play/pause toggle. - media_play_pause = KeyCode.from_vk(0) - - #: The volume mute button. - media_volume_mute = KeyCode.from_vk(0) - - #: The volume down button. - media_volume_down = KeyCode.from_vk(0) - - #: The volume up button. - media_volume_up = KeyCode.from_vk(0) - - #: The previous track button. - media_previous = KeyCode.from_vk(0) - - #: The next track button. - media_next = KeyCode.from_vk(0) - - #: The Insert key. This may be undefined for some platforms. - insert = KeyCode.from_vk(0) - - #: The Menu key. This may be undefined for some platforms. - menu = KeyCode.from_vk(0) - - #: The NumLock key. This may be undefined for some platforms. - num_lock = KeyCode.from_vk(0) - - #: The Pause/Break key. This may be undefined for some platforms. - pause = KeyCode.from_vk(0) - - #: The PrintScreen key. This may be undefined for some platforms. - print_screen = KeyCode.from_vk(0) - - #: The ScrollLock key. This may be undefined for some platforms. - scroll_lock = KeyCode.from_vk(0) - - -class Controller(object): - """A controller for sending virtual keyboard events to the system. - """ - #: The virtual key codes - _KeyCode = KeyCode - - #: The various keys. - _Key = Key - - class InvalidKeyException(Exception): - """The exception raised when an invalid ``key`` parameter is passed to - either :meth:`Controller.press` or :meth:`Controller.release`. - - Its first argument is the ``key`` parameter. - """ - pass - - class InvalidCharacterException(Exception): - """The exception raised when an invalid character is encountered in - the string passed to :meth:`Controller.type`. - - Its first argument is the index of the character in the string, and the - second the character. - """ - pass - - def __init__(self): - self._log = _logger(self.__class__) - self._modifiers_lock = threading.RLock() - self._modifiers = set() - self._caps_lock = False - self._dead_key = None - - def press(self, key): - """Presses a key. - - A key may be either a string of length 1, one of the :class:`Key` - members or a :class:`KeyCode`. - - Strings will be transformed to :class:`KeyCode` using - :meth:`KeyCode.char`. Members of :class:`Key` will be translated to - their :meth:`~Key.value`. - - :param key: The key to press. - - :raises InvalidKeyException: if the key is invalid - - :raises ValueError: if ``key`` is a string, but its length is not ``1`` - """ - resolved = self._resolve(key) - if resolved is None: - raise self.InvalidKeyException(key) - self._update_modifiers(resolved, True) - - # Update caps lock state - if resolved == self._Key.caps_lock.value: - self._caps_lock = not self._caps_lock - - # If we currently have a dead key pressed, join it with this key - original = resolved - if self._dead_key: - try: - resolved = self._dead_key.join(resolved) - except ValueError: - self._handle(self._dead_key, True) - self._handle(self._dead_key, False) - - # If the key is a dead key, keep it for later - if resolved.is_dead: - self._dead_key = resolved - return - - try: - self._handle(resolved, True) - except self.InvalidKeyException: - if resolved != original: - self._handle(self._dead_key, True) - self._handle(self._dead_key, False) - self._handle(original, True) - - self._dead_key = None - - def release(self, key): - """Releases a key. - - A key may be either a string of length 1, one of the :class:`Key` - members or a :class:`KeyCode`. - - Strings will be transformed to :class:`KeyCode` using - :meth:`KeyCode.char`. Members of :class:`Key` will be translated to - their :meth:`~Key.value`. - - :param key: The key to release. If this is a string, it is passed to - :meth:`touches` and the returned releases are used. - - :raises InvalidKeyException: if the key is invalid - - :raises ValueError: if ``key`` is a string, but its length is not ``1`` - """ - resolved = self._resolve(key) - if resolved is None: - raise self.InvalidKeyException(key) - self._update_modifiers(resolved, False) - - # Ignore released dead keys - if resolved.is_dead: - return - - self._handle(resolved, False) - - def tap(self, key): - """Presses and releases a key. - - This is equivalent to the following code:: - - controller.press(key) - controller.release(key) - - :param key: The key to press. - - :raises InvalidKeyException: if the key is invalid - - :raises ValueError: if ``key`` is a string, but its length is not ``1`` - """ - self.press(key) - self.release(key) - - def touch(self, key, is_press): - """Calls either :meth:`press` or :meth:`release` depending on the value - of ``is_press``. - - :param key: The key to press or release. - - :param bool is_press: Whether to press the key. - - :raises InvalidKeyException: if the key is invalid - """ - if is_press: - self.press(key) - else: - self.release(key) - - @contextlib.contextmanager - def pressed(self, *args): - """Executes a block with some keys pressed. - - :param keys: The keys to keep pressed. - """ - for key in args: - self.press(key) - - try: - yield - finally: - for key in reversed(args): - self.release(key) - - def type(self, string): - """Types a string. - - This method will send all key presses and releases necessary to type - all characters in the string. - - :param str string: The string to type. - - :raises InvalidCharacterException: if an untypable character is - encountered - """ - from . import _CONTROL_CODES - for i, character in enumerate(string): - key = _CONTROL_CODES.get(character, character) - try: - self.press(key) - self.release(key) - - except (ValueError, self.InvalidKeyException): - raise self.InvalidCharacterException(i, character) - - @property - @contextlib.contextmanager - def modifiers(self): - """The currently pressed modifier keys. - - Please note that this reflects only the internal state of this - controller, and not the state of the operating system keyboard buffer. - This property cannot be used to determine whether a key is physically - pressed. - - Only the generic modifiers will be set; when pressing either - :attr:`Key.shift_l`, :attr:`Key.shift_r` or :attr:`Key.shift`, only - :attr:`Key.shift` will be present. - - Use this property within a context block thus:: - - with controller.modifiers as modifiers: - with_block() - - This ensures that the modifiers cannot be modified by another thread. - """ - with self._modifiers_lock: - yield set( - self._as_modifier(modifier) - for modifier in self._modifiers) - - @property - def alt_pressed(self): - """Whether any *alt* key is pressed. - - Please note that this reflects only the internal state of this - controller. See :attr:`modifiers` for more information. - """ - with self.modifiers as modifiers: - return self._Key.alt in modifiers - - @property - def alt_gr_pressed(self): - """Whether *altgr* is pressed. - - Please note that this reflects only the internal state of this - controller. See :attr:`modifiers` for more information. - """ - with self.modifiers as modifiers: - return self._Key.alt_gr in modifiers - - @property - def ctrl_pressed(self): - """Whether any *ctrl* key is pressed. - - Please note that this reflects only the internal state of this - controller. See :attr:`modifiers` for more information. - """ - with self.modifiers as modifiers: - return self._Key.ctrl in modifiers - - @property - def shift_pressed(self): - """Whether any *shift* key is pressed, or *caps lock* is toggled. - - Please note that this reflects only the internal state of this - controller. See :attr:`modifiers` for more information. - """ - if self._caps_lock: - return True - - with self.modifiers as modifiers: - return self._Key.shift in modifiers - - def _resolve(self, key): - """Resolves a key to a :class:`KeyCode` instance. - - This method will convert any key representing a character to uppercase - if a shift modifier is active. - - :param key: The key to resolve. - - :return: a key code, or ``None`` if it cannot be resolved - """ - # Use the value for the key constants - if key in (k for k in self._Key): - return key.value - - # Convert strings to key codes - if isinstance(key, six.string_types): - if len(key) != 1: - raise ValueError(key) - return self._KeyCode.from_char(key) - - # Assume this is a proper key - if isinstance(key, self._KeyCode): - if key.char is not None and self.shift_pressed: - return self._KeyCode(vk=key.vk, char=key.char.upper()) - else: - return key - - def _update_modifiers(self, key, is_press): - """Updates the current modifier list. - - If ``key`` is not a modifier, no action is taken. - - :param key: The key being pressed or released. - """ - # Check whether the key is a modifier - if self._as_modifier(key): - with self._modifiers_lock: - if is_press: - self._modifiers.add(key) - else: - try: - self._modifiers.remove(key) - except KeyError: - pass - - def _as_modifier(self, key): - """Returns a key as the modifier used internally if defined. - - This method will convert values like :attr:`Key.alt_r.value` and - :attr:`Key.shift_l.value` to :attr:`Key.alt` and :attr:`Key.shift`. - - :param key: The possible modifier key. - - :return: the base modifier key, or ``None`` if ``key`` is not a - modifier - """ - from . import _NORMAL_MODIFIERS - return _NORMAL_MODIFIERS.get(key, None) - - def _handle(self, key, is_press): - """The platform implementation of the actual emitting of keyboard - events. - - This is a platform dependent implementation. - - :param Key key: The key to handle. - - :param bool is_press: Whether this is a key press event. - """ - raise NotImplementedError() - - -# pylint: disable=W0223; This is also an abstract class -class Listener(AbstractListener): - """A listener for keyboard events. - - Instances of this class can be used as context managers. This is equivalent - to the following code:: - - listener.start() - try: - listener.wait() - with_statements() - finally: - listener.stop() - - This class inherits from :class:`threading.Thread` and supports all its - methods. It will set :attr:`daemon` to ``True`` when created. - - All callback arguments are optional; a callback may take less arguments - than actually passed, but not more, unless they are optional. - - :param callable on_press: The callback to call when a button is pressed. - - It will be called with the arguments ``(key, injected)``, where ``key`` - is a :class:`KeyCode`, a :class:`Key` or ``None`` if the key is - unknown, and ``injected`` whether the event was injected and thus not - generated by an actual input device. - - Please note that not all backends support ``injected`` and will always - set it to ``False``. - - :param callable on_release: The callback to call when a button is released. - - It will be called with the arguments ``(key, injected)``, where ``key`` - is a :class:`KeyCode`, a :class:`Key` or ``None`` if the key is - unknown, and ``injected`` whether the event was injected and thus not - generated by an actual input device. - - Please note that not all backends support ``injected`` and will always - set it to ``False``. - - :param bool suppress: Whether to suppress events. Setting this to ``True`` - will prevent the input events from being passed to the rest of the - system. - - :param kwargs: Any non-standard platform dependent options. These should be - prefixed with the platform name thus: ``darwin_``, ``uinput_``, - ``xorg_`` or ``win32_``. - - Supported values are: - - ``darwin_intercept`` - A callable taking the arguments ``(event_type, event)``, where - ``event_type`` is ``Quartz.kCGEventKeyDown`` or - ``Quartz.kCGEventKeyUp``, and ``event`` is a ``CGEventRef``. - - This callable can freely modify the event using functions like - ``Quartz.CGEventSetIntegerValueField``. If this callable does not - return the event, the event is suppressed system wide. - - ``uinput_device_paths`` - A list of device paths. - - If this is specified, *pynput* will limit the number of devices - checked for the capabilities needed to those passed, otherwise all - system devices will be used. Passing this might be required if an - incorrect device is chosen. - - ``win32_event_filter`` - A callable taking the arguments ``(msg, data)``, where ``msg`` is - the current message, and ``data`` associated data as a - `KBDLLHOOKSTRUCT `_. - - If this callback returns ``False``, the event will not be - propagated to the listener callback. - - If ``self.suppress_event()`` is called, the event is suppressed - system wide. - """ - def __init__(self, on_press=None, on_release=None, suppress=False, - **kwargs): - self._log = _logger(self.__class__) - option_prefix = prefix(Listener, self.__class__) - self._options = { - key[len(option_prefix):]: value - for key, value in kwargs.items() - if key.startswith(option_prefix)} - super(Listener, self).__init__( - on_press=self._wrap(on_press, 2), - on_release=self._wrap(on_release, 2), - suppress=suppress) -# pylint: enable=W0223 - - def canonical(self, key): - """Performs normalisation of a key. - - This method attempts to convert key events to their canonical form, so - that events will equal regardless of modifier state. - - This method will convert upper case keys to lower case keys, convert - any modifiers with a right and left version to the same value, and may - slow perform additional platform dependent normalisation. - - :param key: The key to normalise. - :type key: Key or KeyCode - - :return: a key - :rtype: Key or KeyCode - """ - from pynput.keyboard import Key, KeyCode, _NORMAL_MODIFIERS - if isinstance(key, KeyCode) and key.char is not None: - return KeyCode.from_char(key.char.lower()) - elif isinstance(key, Key) and key.value in _NORMAL_MODIFIERS: - return _NORMAL_MODIFIERS[key.value] - elif isinstance(key, Key) and key.value.vk is not None: - return KeyCode.from_vk(key.value.vk) - else: - return key diff --git a/PortablePython/Lib/site-packages/pynput/keyboard/_darwin.py b/PortablePython/Lib/site-packages/pynput/keyboard/_darwin.py deleted file mode 100644 index 4065164..0000000 --- a/PortablePython/Lib/site-packages/pynput/keyboard/_darwin.py +++ /dev/null @@ -1,367 +0,0 @@ -# coding=utf-8 -# pynput -# Copyright (C) 2015-2024 Moses Palmér -# -# This program 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 3 of the License, or (at your option) any -# later version. -# -# This program 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 program. If not, see . -""" -The keyboard implementation for *macOS*. -""" - -# pylint: disable=C0111 -# The documentation is extracted from the base classes - -# pylint: disable=R0903 -# We implement stubs - -import enum - -from Quartz import ( - CGEventCreateKeyboardEvent, - CGEventGetFlags, - CGEventGetIntegerValueField, - CGEventGetType, - CGEventKeyboardGetUnicodeString, - CGEventKeyboardSetUnicodeString, - CGEventMaskBit, - CGEventPost, - CGEventSetFlags, - kCGEventFlagMaskAlternate, - kCGEventFlagMaskCommand, - kCGEventFlagMaskControl, - kCGEventFlagMaskShift, - kCGEventFlagsChanged, - kCGEventKeyDown, - kCGEventKeyUp, - kCGHIDEventTap, - kCGKeyboardEventKeycode, - NSEvent, - NSSystemDefined) - -from pynput._util.darwin import ( - get_unicode_to_keycode_map, - keycode_context, - ListenerMixin) -from pynput._util.darwin_vks import SYMBOLS -from . import _base - - -# From hidsystem/ev_keymap.h -NX_KEYTYPE_PLAY = 16 -NX_KEYTYPE_MUTE = 7 -NX_KEYTYPE_SOUND_DOWN = 1 -NX_KEYTYPE_SOUND_UP = 0 -NX_KEYTYPE_NEXT = 17 -NX_KEYTYPE_PREVIOUS = 18 -NX_KEYTYPE_EJECT = 14 - -# pylint: disable=C0103; We want to use the names from the C API -# This is undocumented, but still widely known -kSystemDefinedEventMediaKeysSubtype = 8 - -# We extract this here since the name is very long -otherEventWithType = getattr( - NSEvent, - 'otherEventWithType_' - 'location_' - 'modifierFlags_' - 'timestamp_' - 'windowNumber_' - 'context_' - 'subtype_' - 'data1_' - 'data2_') -# pylint: enable=C0103 - - -class KeyCode(_base.KeyCode): - _PLATFORM_EXTENSIONS = ( - # Whether this is a media key - '_is_media', - ) - - # Be explicit about fields - _is_media = None - - @classmethod - def _from_media(cls, vk, **kwargs): - """Creates a media key from a key code. - - :param int vk: The key code. - - :return: a key code - """ - return cls.from_vk(vk, _is_media=True, **kwargs) - - def _event(self, modifiers, mapping, is_pressed): - """This key as a *Quartz* event. - - :param set modifiers: The currently active modifiers. - - :param mapping: The current keyboard mapping. - - :param bool is_press: Whether to generate a press event. - - :return: a *Quartz* event - """ - vk = self.vk or mapping.get(self.char) - if self._is_media: - result = otherEventWithType( - NSSystemDefined, - (0, 0), - 0xa00 if is_pressed else 0xb00, - 0, - 0, - 0, - 8, - (self.vk << 16) | ((0xa if is_pressed else 0xb) << 8), - -1).CGEvent() - else: - result = CGEventCreateKeyboardEvent( - None, 0 if vk is None else vk, is_pressed) - - CGEventSetFlags( - result, - 0 - | (kCGEventFlagMaskAlternate - if Key.alt in modifiers else 0) - - | (kCGEventFlagMaskCommand - if Key.cmd in modifiers else 0) - - | (kCGEventFlagMaskControl - if Key.ctrl in modifiers else 0) - - | (kCGEventFlagMaskShift - if Key.shift in modifiers else 0)) - - if vk is None and self.char is not None: - CGEventKeyboardSetUnicodeString( - result, len(self.char), self.char) - - return result - - -# pylint: disable=W0212 -class Key(enum.Enum): - # Default keys - alt = KeyCode.from_vk(0x3A) - alt_l = KeyCode.from_vk(0x3A) - alt_r = KeyCode.from_vk(0x3D) - alt_gr = KeyCode.from_vk(0x3D) - backspace = KeyCode.from_vk(0x33) - caps_lock = KeyCode.from_vk(0x39) - cmd = KeyCode.from_vk(0x37) - cmd_l = KeyCode.from_vk(0x37) - cmd_r = KeyCode.from_vk(0x36) - ctrl = KeyCode.from_vk(0x3B) - ctrl_l = KeyCode.from_vk(0x3B) - ctrl_r = KeyCode.from_vk(0x3E) - delete = KeyCode.from_vk(0x75) - down = KeyCode.from_vk(0x7D) - end = KeyCode.from_vk(0x77) - enter = KeyCode.from_vk(0x24) - esc = KeyCode.from_vk(0x35) - f1 = KeyCode.from_vk(0x7A) - f2 = KeyCode.from_vk(0x78) - f3 = KeyCode.from_vk(0x63) - f4 = KeyCode.from_vk(0x76) - f5 = KeyCode.from_vk(0x60) - f6 = KeyCode.from_vk(0x61) - f7 = KeyCode.from_vk(0x62) - f8 = KeyCode.from_vk(0x64) - f9 = KeyCode.from_vk(0x65) - f10 = KeyCode.from_vk(0x6D) - f11 = KeyCode.from_vk(0x67) - f12 = KeyCode.from_vk(0x6F) - f13 = KeyCode.from_vk(0x69) - f14 = KeyCode.from_vk(0x6B) - f15 = KeyCode.from_vk(0x71) - f16 = KeyCode.from_vk(0x6A) - f17 = KeyCode.from_vk(0x40) - f18 = KeyCode.from_vk(0x4F) - f19 = KeyCode.from_vk(0x50) - f20 = KeyCode.from_vk(0x5A) - home = KeyCode.from_vk(0x73) - left = KeyCode.from_vk(0x7B) - page_down = KeyCode.from_vk(0x79) - page_up = KeyCode.from_vk(0x74) - right = KeyCode.from_vk(0x7C) - shift = KeyCode.from_vk(0x38) - shift_l = KeyCode.from_vk(0x38) - shift_r = KeyCode.from_vk(0x3C) - space = KeyCode.from_vk(0x31, char=' ') - tab = KeyCode.from_vk(0x30) - up = KeyCode.from_vk(0x7E) - - media_play_pause = KeyCode._from_media(NX_KEYTYPE_PLAY) - media_volume_mute = KeyCode._from_media(NX_KEYTYPE_MUTE) - media_volume_down = KeyCode._from_media(NX_KEYTYPE_SOUND_DOWN) - media_volume_up = KeyCode._from_media(NX_KEYTYPE_SOUND_UP) - media_previous = KeyCode._from_media(NX_KEYTYPE_PREVIOUS) - media_next = KeyCode._from_media(NX_KEYTYPE_NEXT) - media_eject = KeyCode._from_media(NX_KEYTYPE_EJECT) -# pylint: enable=W0212 - - -class Controller(_base.Controller): - _KeyCode = KeyCode - _Key = Key - - def __init__(self): - super(Controller, self).__init__() - self._mapping = get_unicode_to_keycode_map() - - def _handle(self, key, is_press): - with self.modifiers as modifiers: - CGEventPost( - kCGHIDEventTap, - (key if key not in (k for k in Key) else key.value)._event( - modifiers, self._mapping, is_press)) - - -class Listener(ListenerMixin, _base.Listener): - #: The events that we listen to - _EVENTS = ( - CGEventMaskBit(kCGEventKeyDown) | - CGEventMaskBit(kCGEventKeyUp) | - CGEventMaskBit(kCGEventFlagsChanged) | - CGEventMaskBit(NSSystemDefined) - ) - - # pylint: disable=W0212 - #: A mapping from keysym to special key - _SPECIAL_KEYS = { - (key.value.vk, key.value._is_media): key - for key in Key} - # pylint: enable=W0212 - - #: The event flags set for the various modifier keys - _MODIFIER_FLAGS = { - Key.alt: kCGEventFlagMaskAlternate, - Key.alt_l: kCGEventFlagMaskAlternate, - Key.alt_r: kCGEventFlagMaskAlternate, - Key.cmd: kCGEventFlagMaskCommand, - Key.cmd_l: kCGEventFlagMaskCommand, - Key.cmd_r: kCGEventFlagMaskCommand, - Key.ctrl: kCGEventFlagMaskControl, - Key.ctrl_l: kCGEventFlagMaskControl, - Key.ctrl_r: kCGEventFlagMaskControl, - Key.shift: kCGEventFlagMaskShift, - Key.shift_l: kCGEventFlagMaskShift, - Key.shift_r: kCGEventFlagMaskShift} - - def __init__(self, *args, **kwargs): - super(Listener, self).__init__(*args, **kwargs) - self._flags = 0 - self._context = None - self._intercept = self._options.get( - 'intercept', - None) - - def _run(self): - with keycode_context() as context: - self._context = context - try: - super(Listener, self)._run() - finally: - self._context = None - - def _handle_message(self, _proxy, event_type, event, _refcon, injected): - # Convert the event to a KeyCode; this may fail, and in that case we - # pass None - try: - key = self._event_to_key(event) - except IndexError: - key = None - - try: - if event_type == kCGEventKeyDown: - # This is a normal key press - self.on_press(key, injected) - - elif event_type == kCGEventKeyUp: - # This is a normal key release - self.on_release(key, injected) - - elif key == Key.caps_lock: - # We only get an event when caps lock is toggled, so we fake - # press and release - self.on_press(key, injected) - self.on_release(key, injected) - - elif event_type == NSSystemDefined: - sys_event = NSEvent.eventWithCGEvent_(event) - if sys_event.subtype() == kSystemDefinedEventMediaKeysSubtype: - # The key in the special key dict; True since it is a media - # key - key = ((sys_event.data1() & 0xffff0000) >> 16, True) - if key in self._SPECIAL_KEYS: - flags = sys_event.data1() & 0x0000ffff - is_press = ((flags & 0xff00) >> 8) == 0x0a - if is_press: - self.on_press(self._SPECIAL_KEYS[key]) - else: - self.on_release(self._SPECIAL_KEYS[key]) - - else: - # This is a modifier event---excluding caps lock---for which we - # must check the current modifier state to determine whether - # the key was pressed or released - flags = CGEventGetFlags(event) - is_press = flags & self._MODIFIER_FLAGS.get(key, 0) - if is_press: - self.on_press(key, injected) - else: - self.on_release(key, injected) - - finally: - # Store the current flag mask to be able to detect modifier state - # changes - self._flags = CGEventGetFlags(event) - - def _event_to_key(self, event): - """Converts a *Quartz* event to a :class:`KeyCode`. - - :param event: The event to convert. - - :return: a :class:`pynput.keyboard.KeyCode` - - :raises IndexError: if the key code is invalid - """ - vk = CGEventGetIntegerValueField( - event, kCGKeyboardEventKeycode) - event_type = CGEventGetType(event) - is_media = True if event_type == NSSystemDefined else None - - # First try special keys... - key = (vk, is_media) - if key in self._SPECIAL_KEYS: - return self._SPECIAL_KEYS[key] - - # ...then try characters... - length, chars = CGEventKeyboardGetUnicodeString( - event, 100, None, None) - try: - printable = chars.isprintable() - except AttributeError: - printable = chars.isalnum() - if not printable and vk in SYMBOLS \ - and CGEventGetFlags(event) \ - & kCGEventFlagMaskControl: - return KeyCode.from_char(SYMBOLS[vk], vk=vk) - elif length > 0: - return KeyCode.from_char(chars, vk=vk) - - # ...and fall back on a virtual key code - return KeyCode.from_vk(vk) diff --git a/PortablePython/Lib/site-packages/pynput/keyboard/_dummy.py b/PortablePython/Lib/site-packages/pynput/keyboard/_dummy.py deleted file mode 100644 index 10727f6..0000000 --- a/PortablePython/Lib/site-packages/pynput/keyboard/_dummy.py +++ /dev/null @@ -1,23 +0,0 @@ -# coding=utf-8 -# pynput -# Copyright (C) 2015-2024 Moses Palmér -# -# This program 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 3 of the License, or (at your option) any -# later version. -# -# This program 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 program. If not, see . -""" -This module contains a dummy implementation. - -It cannot be used, but importing it will not raise any exceptions. -""" - -from ._base import Controller, Key, KeyCode, Listener diff --git a/PortablePython/Lib/site-packages/pynput/keyboard/_uinput.py b/PortablePython/Lib/site-packages/pynput/keyboard/_uinput.py deleted file mode 100644 index 2f63dd3..0000000 --- a/PortablePython/Lib/site-packages/pynput/keyboard/_uinput.py +++ /dev/null @@ -1,446 +0,0 @@ -# coding=utf-8 -# pynput -# Copyright (C) 2015-2024 Moses Palmér -# -# This program 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 3 of the License, or (at your option) any -# later version. -# -# This program 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 program. If not, see . -""" -The keyboard implementation for *uinput*. -""" - -# pylint: disable=C0111 -# The documentation is extracted from the base classes - -# pylint: disable=R0903 -# We implement stubs - -import enum -import errno -import functools -import os -import re -import subprocess - -import evdev - -from evdev.events import KeyEvent - -from pynput._util import xorg_keysyms -from pynput._util.uinput import ListenerMixin -from . import _base - - -class KeyCode(_base.KeyCode): - _PLATFORM_EXTENSIONS = ( - # The name for this key - '_x_name', - '_kernel_name', - ) - - # Be explicit about fields - _x_name = None - _kernel_name = None -# pylint: enable=W0212 - - @classmethod - def _from_name(cls, x_name, kernel_name, **kwargs): - """Creates a key from a name. - - :param str x_name: The X name. - - :param str kernel_name: The kernel name. - - :return: a key code - """ - try: - vk = getattr(evdev.ecodes, kernel_name) - except AttributeError: - vk = None - return cls.from_vk( - vk, _x_name=x_name, _kernel_name=kernel_name, **kwargs) - - -# pylint: disable=W0212 -class Key(enum.Enum): - alt = KeyCode._from_name('Alt_L', 'KEY_LEFTALT') - alt_l = KeyCode._from_name('Alt_L', 'KEY_LEFTALT') - alt_r = KeyCode._from_name('Alt_R', 'KEY_RIGHTALT') - alt_gr = KeyCode._from_name('Mode_switch', 'KEY_RIGHTALT') - backspace = KeyCode._from_name('BackSpace', 'KEY_BACKSPACE') - caps_lock = KeyCode._from_name('Caps_Lock', 'KEY_CAPSLOCK') - cmd = KeyCode._from_name('Super_L', 'KEY_LEFTMETA') - cmd_l = KeyCode._from_name('Super_L', 'KEY_LEFTMETA') - cmd_r = KeyCode._from_name('Super_R', 'KEY_RIGHTMETA') - ctrl = KeyCode._from_name('Control_L', 'KEY_LEFTCTRL') - ctrl_l = KeyCode._from_name('Control_L', 'KEY_LEFTCTRL') - ctrl_r = KeyCode._from_name('Control_R', 'KEY_RIGHTCTRL') - delete = KeyCode._from_name('Delete', 'KEY_DELETE') - down = KeyCode._from_name('Down', 'KEY_DOWN') - end = KeyCode._from_name('End', 'KEY_END') - enter = KeyCode._from_name('Return', 'KEY_ENTER') - esc = KeyCode._from_name('Escape', 'KEY_ESC') - f1 = KeyCode._from_name('F1', 'KEY_F1') - f2 = KeyCode._from_name('F2', 'KEY_F2') - f3 = KeyCode._from_name('F3', 'KEY_F3') - f4 = KeyCode._from_name('F4', 'KEY_F4') - f5 = KeyCode._from_name('F5', 'KEY_F5') - f6 = KeyCode._from_name('F6', 'KEY_F6') - f7 = KeyCode._from_name('F7', 'KEY_F7') - f8 = KeyCode._from_name('F8', 'KEY_F8') - f9 = KeyCode._from_name('F9', 'KEY_F9') - f10 = KeyCode._from_name('F10', 'KEY_F10') - f11 = KeyCode._from_name('F11', 'KEY_F11') - f12 = KeyCode._from_name('F12', 'KEY_F12') - f13 = KeyCode._from_name('F13', 'KEY_F13') - f14 = KeyCode._from_name('F14', 'KEY_F14') - f15 = KeyCode._from_name('F15', 'KEY_F15') - f16 = KeyCode._from_name('F16', 'KEY_F16') - f17 = KeyCode._from_name('F17', 'KEY_F17') - f18 = KeyCode._from_name('F18', 'KEY_F18') - f19 = KeyCode._from_name('F19', 'KEY_F19') - f20 = KeyCode._from_name('F20', 'KEY_F20') - home = KeyCode._from_name('Home', 'KEY_HOME') - left = KeyCode._from_name('Left', 'KEY_LEFT') - page_down = KeyCode._from_name('Page_Down', 'KEY_PAGEDOWN') - page_up = KeyCode._from_name('Page_Up', 'KEY_PAGEUP') - right = KeyCode._from_name('Right', 'KEY_RIGHT') - shift = KeyCode._from_name('Shift_L', 'KEY_LEFTSHIFT') - shift_l = KeyCode._from_name('Shift_L', 'KEY_LEFTSHIFT') - shift_r = KeyCode._from_name('Shift_R', 'KEY_RIGHTSHIFT') - space = KeyCode._from_name('space', 'KEY_SPACE', char=' ') - tab = KeyCode._from_name('Tab', 'KEY_TAB', char='\t') - up = KeyCode._from_name('Up', 'KEY_UP') - - media_play_pause = KeyCode._from_name('Play', 'KEY_PLAYPAUSE') - media_volume_mute = KeyCode._from_name('Mute', 'KEY_MUTE') - media_volume_down = KeyCode._from_name('LowerVolume', 'KEY_VOLUMEDOWN') - media_volume_up = KeyCode._from_name('RaiseVolume', 'KEY_VOLUMEUP') - media_previous = KeyCode._from_name('Prev', 'KEY_PREVIOUSSONG') - media_next = KeyCode._from_name('Next', 'KEY_NEXTSONG') - - insert = KeyCode._from_name('Insert', 'KEY_INSERT') - menu = KeyCode._from_name('Menu', 'KEY_MENU') - num_lock = KeyCode._from_name('Num_Lock', 'KEY_NUMLOCK') - pause = KeyCode._from_name('Pause', 'KEY_PAUSE') - print_screen = KeyCode._from_name('Print', 'KEY_SYSRQ') - scroll_lock = KeyCode._from_name('Scroll_Lock', 'KEY_SCROLLLOCK') -# pylint: enable=W0212 - - -class Layout(object): - """A description of the keyboard layout. - """ - #: A regular expression to parse keycodes in the dumpkeys output - #: - #: The groups are: keycode number, key names. - KEYCODE_RE = re.compile( - r'keycode\s+(\d+)\s+=(.*)') - - class Key(object): - """A key in a keyboard layout. - """ - def __init__(self, normal, shifted, alt, alt_shifted): - self._values = ( - normal, - shifted, - alt, - alt_shifted) - - def __str__(self): - return ('<' - 'normal: {}, ' - 'shifted: {}, ' - 'alternative: {}, ' - 'shifted alternative: {}>').format( - self.normal, self.shifted, self.alt, self.alt_shifted) - - __repr__ = __str__ - - def __iter__(self): - return iter(self._values) - - def __getitem__(self, i): - return self._values[i] - - @property - def normal(self): - """The normal key. - """ - return self._values[0] - - @property - def shifted(self): - """The shifted key. - """ - return self._values[1] - - @property - def alt(self): - """The alternative key. - """ - return self._values[2] - - @property - def alt_shifted(self): - """The shifted alternative key. - """ - return self._values[3] - - def __init__(self): - def as_char(k): - return k.value.char if isinstance(k, Key) else k.char - self._vk_table = self._load() - self._char_table = { - as_char(key): ( - vk, - set() - | {Key.shift} if i & 1 else set() - | {Key.alt_gr} if i & 2 else set()) - for vk, keys in self._vk_table.items() - for i, key in enumerate(keys) - if key is not None and as_char(key) is not None} - - def for_vk(self, vk, modifiers): - """Reads a key for a virtual key code and modifier state. - - :param int vk: The virtual key code. - - :param set modifiers: A set of modifiers. - - :return: a mapped key - - :raises KeyError: if ``vk`` is an unknown key - """ - return self._vk_table[vk][ - 0 - | (1 if Key.shift in modifiers else 0) - | (2 if Key.alt_gr in modifiers else 0)] - - def for_char(self, char): - """Reads a virtual key code and modifier state for a character. - - :param str char: The character. - - :return: the tuple ``(vk, modifiers)`` - - :raises KeyError: if ``vk`` is an unknown key - """ - return self._char_table[char] - - @functools.lru_cache() - def _load(self): - """Loads the keyboard layout. - - For simplicity, we call out to the ``dumpkeys`` binary. In the future, - we may want to implement this ourselves. - """ - result = {} - for keycode, names in self.KEYCODE_RE.findall( - subprocess.check_output( - ['dumpkeys', '--full-table', '--keys-only']).decode('utf-8')): - vk = int(keycode) - keys = tuple( - self._parse(vk, name) - for name in names.split()[:4]) - if any(key is not None for key in keys): - result[vk] = self.Key(*keys) - return result - - def _parse(self, vk, name): - """Parses a single key from the ``dumpkeys`` output. - - :param int vk: The key code. - - :param str name: The key name. - - :return: a key representation - """ - try: - # First try special keys... - return next( - key - for key in Key - if key.value._x_name == name) - except StopIteration: - # ...then characters... - try: - _, char = xorg_keysyms.SYMBOLS[name.lstrip('+')] - if char: - return KeyCode.from_char(char, vk=vk) - except KeyError: - pass - - # ...and finally special dumpkeys names - try: - return KeyCode.from_char({ - 'one': '1', - 'two': '2', - 'three': '3', - 'four': '4', - 'five': '5', - 'six': '6', - 'seven': '7', - 'eight': '8', - 'nine': '9', - 'zero': '0'}[name]) - except KeyError: - pass - - -class Controller(_base.Controller): - _KeyCode = KeyCode - _Key = Key - - def __init__(self, *args, **kwargs): - super(Controller, self).__init__(*args, **kwargs) - self._layout = LAYOUT - self._dev = evdev.UInput() - - def __del__(self): - if hasattr(self, '_dev'): - self._dev.close() - - def _handle(self, key, is_press): - # Resolve the key to a virtual key code and a possible set of required - # modifiers - try: - vk, required_modifiers = self._to_vk_and_modifiers(key) - except ValueError: - raise self.InvalidKeyException(key) - - # Determine how we need to modify the modifier state - if is_press and required_modifiers is not None: - with self.modifiers as modifiers: - vk, required_modifiers = self._layout.for_char(key.char) - to_press = { - getattr(evdev.ecodes, key.value._kernel_name) - for key in (required_modifiers - modifiers)} - to_release = { - getattr(evdev.ecodes, key.value._kernel_name) - for key in (modifiers - required_modifiers)} - else: - to_release = set() - to_press = set() - - # Update the modifier state, send the key, and finally release any - # modifiers - cleanup = [] - try: - for k in to_release: - self._send(k, False) - cleanup.append((k, True)) - for k in to_press: - self._send(k, True) - cleanup.append((k, False)) - - self._send(vk, is_press) - - finally: - for e in reversed(cleanup): - # pylint: disable E722; we want to suppress exceptions - try: - self._send(*e) - except: - pass - # pylint: enable E722 - - self._dev.syn() - - def _to_vk_and_modifiers(self, key): - """Resolves a key to a virtual key code and a modifier set. - - :param key: The key to resolve. - :type key: Key or KeyCode - - :return: a virtual key code and possible required modifiers - """ - if hasattr(key, 'vk') and key.vk is not None: - return (key.vk, None) - elif hasattr(key, 'char') and key.char is not None: - return self._layout.for_char(key.char) - else: - raise ValueError(key) - - def _send(self, vk, is_press): - """Sends a virtual key event. - - This method does not perform ``SYN``. - - :param int vk: The virtual key. - - :param bool is_press: Whether this is a press event. - """ - self._dev.write(evdev.ecodes.EV_KEY, vk, int(is_press)) - - -class Listener(ListenerMixin, _base.Listener): - _EVENTS = ( - evdev.ecodes.EV_KEY,) - - #: A - _MODIFIERS = { - Key.alt.value.vk: Key.alt, - Key.alt_l.value.vk: Key.alt, - Key.alt_r.value.vk: Key.alt, - Key.alt_gr.value.vk: Key.alt_gr, - Key.shift.value.vk: Key.shift, - Key.shift_l.value.vk: Key.shift, - Key.shift_r.value.vk: Key.shift} - - def __init__(self, *args, **kwargs): - super(Listener, self).__init__(*args, **kwargs) - self._layout = LAYOUT - self._modifiers = set() - - def _handle_message(self, event): - is_press = event.value in (KeyEvent.key_down, KeyEvent.key_hold) - vk = event.code - - # Update the modifier state - if vk in self._MODIFIERS: - modifier = self._MODIFIERS[vk] - if is_press: - self._modifiers.add(modifier) - elif modifier in self._modifiers: - self._modifiers.remove(modifier) - - # Attempt to map the virtual key code to a key - try: - key = self._layout.for_vk(vk, self._modifiers) - except KeyError: - try: - key = next( - key - for key in Key - if key.value.vk == vk) - except StopIteration: - key = KeyCode.from_vk(vk) - - # We do not know whether these events are injected - if is_press: - self.on_press(key, False) - else: - self.on_release(key, False) - - -try: - #: The keyboard layout. - LAYOUT = Layout() -except subprocess.CalledProcessError as e: - raise ImportError('failed to load keyboard layout: "' + str(e) + ( - '"; please make sure you are root' if os.getuid() != 1 else '"')) -except OSError as e: - raise ImportError({ - errno.ENOENT: 'the binary dumpkeys is not installed'}.get( - e.args[0], - str(e))) diff --git a/PortablePython/Lib/site-packages/pynput/keyboard/_win32.py b/PortablePython/Lib/site-packages/pynput/keyboard/_win32.py deleted file mode 100644 index fbbcc98..0000000 --- a/PortablePython/Lib/site-packages/pynput/keyboard/_win32.py +++ /dev/null @@ -1,389 +0,0 @@ -# coding=utf-8 -# pynput -# Copyright (C) 2015-2024 Moses Palmér -# -# This program 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 3 of the License, or (at your option) any -# later version. -# -# This program 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 program. If not, see . -""" -The keyboard implementation for *Windows*. -""" - -# pylint: disable=C0111 -# The documentation is extracted from the base classes - -# pylint: disable=R0903 -# We implement stubs - -import contextlib -import ctypes -import enum -import six - -from ctypes import wintypes - -import pynput._util.win32_vks as VK - -from pynput._util import AbstractListener -from pynput._util.win32 import ( - INPUT, - INPUT_union, - KEYBDINPUT, - KeyTranslator, - ListenerMixin, - MapVirtualKey, - SendInput, - SystemHook, - VkKeyScan) -from . import _base - - -class KeyCode(_base.KeyCode): - _PLATFORM_EXTENSIONS = ( - # Any extra flags. - '_flags', - - #: The scan code. - '_scan', - ) - - # Be explicit about fields - _flags = None - _scan = None - - def _parameters(self, is_press): - """The parameters to pass to ``SendInput`` to generate this key. - - :param bool is_press: Whether to generate a press event. - - :return: all arguments to pass to ``SendInput`` for this key - - :rtype: dict - - :raise ValueError: if this key is a unicode character that cannot be - represented by a single UTF-16 value - """ - if self.vk: - vk = self.vk - scan = self._scan \ - or MapVirtualKey(vk, MapVirtualKey.MAPVK_VK_TO_VSC) - flags = 0 - elif ord(self.char) > 0xFFFF: - raise ValueError - else: - res = VkKeyScan(self.char) - if (res >> 8) & 0xFF == 0: - vk = res & 0xFF - scan = self._scan \ - or MapVirtualKey(vk, MapVirtualKey.MAPVK_VK_TO_VSC) - flags = 0 - else: - vk = 0 - scan = ord(self.char) - flags = KEYBDINPUT.UNICODE - state_flags = (KEYBDINPUT.KEYUP if not is_press else 0) - return dict( - dwFlags=(self._flags or 0) | flags | state_flags, - wVk=vk, - wScan=scan) - - @classmethod - def _from_ext(cls, vk, **kwargs): - """Creates an extended key code. - - :param vk: The virtual key code. - - :param kwargs: Any other parameters to pass. - - :return: a key code - """ - return cls.from_vk(vk, _flags=KEYBDINPUT.EXTENDEDKEY, **kwargs) - - -# pylint: disable=W0212 -class Key(enum.Enum): - alt = KeyCode.from_vk(VK.MENU) - alt_l = KeyCode.from_vk(VK.LMENU) - alt_r = KeyCode._from_ext(VK.RMENU) - alt_gr = KeyCode.from_vk(VK.RMENU) - backspace = KeyCode.from_vk(VK.BACK) - caps_lock = KeyCode.from_vk(VK.CAPITAL) - cmd = KeyCode.from_vk(VK.LWIN) - cmd_l = KeyCode.from_vk(VK.LWIN) - cmd_r = KeyCode.from_vk(VK.RWIN) - ctrl = KeyCode.from_vk(VK.CONTROL) - ctrl_l = KeyCode.from_vk(VK.LCONTROL) - ctrl_r = KeyCode._from_ext(VK.RCONTROL) - delete = KeyCode._from_ext(VK.DELETE) - down = KeyCode._from_ext(VK.DOWN) - end = KeyCode._from_ext(VK.END) - enter = KeyCode.from_vk(VK.RETURN) - esc = KeyCode.from_vk(VK.ESCAPE) - f1 = KeyCode.from_vk(VK.F1) - f2 = KeyCode.from_vk(VK.F2) - f3 = KeyCode.from_vk(VK.F3) - f4 = KeyCode.from_vk(VK.F4) - f5 = KeyCode.from_vk(VK.F5) - f6 = KeyCode.from_vk(VK.F6) - f7 = KeyCode.from_vk(VK.F7) - f8 = KeyCode.from_vk(VK.F8) - f9 = KeyCode.from_vk(VK.F9) - f10 = KeyCode.from_vk(VK.F10) - f11 = KeyCode.from_vk(VK.F11) - f12 = KeyCode.from_vk(VK.F12) - f13 = KeyCode.from_vk(VK.F13) - f14 = KeyCode.from_vk(VK.F14) - f15 = KeyCode.from_vk(VK.F15) - f16 = KeyCode.from_vk(VK.F16) - f17 = KeyCode.from_vk(VK.F17) - f18 = KeyCode.from_vk(VK.F18) - f19 = KeyCode.from_vk(VK.F19) - f20 = KeyCode.from_vk(VK.F20) - f21 = KeyCode.from_vk(VK.F21) - f22 = KeyCode.from_vk(VK.F22) - f23 = KeyCode.from_vk(VK.F23) - f24 = KeyCode.from_vk(VK.F24) - home = KeyCode._from_ext(VK.HOME) - left = KeyCode._from_ext(VK.LEFT) - page_down = KeyCode._from_ext(VK.NEXT) - page_up = KeyCode._from_ext(VK.PRIOR) - right = KeyCode._from_ext(VK.RIGHT) - shift = KeyCode.from_vk(VK.LSHIFT) - shift_l = KeyCode.from_vk(VK.LSHIFT) - shift_r = KeyCode.from_vk(VK.RSHIFT) - space = KeyCode.from_vk(VK.SPACE, char=' ') - tab = KeyCode.from_vk(VK.TAB) - up = KeyCode._from_ext(VK.UP) - - media_play_pause = KeyCode._from_ext(VK.MEDIA_PLAY_PAUSE) - media_stop = KeyCode._from_ext(VK.MEDIA_STOP) - media_volume_mute = KeyCode._from_ext(VK.VOLUME_MUTE) - media_volume_down = KeyCode._from_ext(VK.VOLUME_DOWN) - media_volume_up = KeyCode._from_ext(VK.VOLUME_UP) - media_previous = KeyCode._from_ext(VK.MEDIA_PREV_TRACK) - media_next = KeyCode._from_ext(VK.MEDIA_NEXT_TRACK) - - insert = KeyCode._from_ext(VK.INSERT) - menu = KeyCode.from_vk(VK.APPS) - num_lock = KeyCode._from_ext(VK.NUMLOCK) - pause = KeyCode.from_vk(VK.PAUSE) - print_screen = KeyCode._from_ext(VK.SNAPSHOT) - scroll_lock = KeyCode.from_vk(VK.SCROLL) -# pylint: enable=W0212 - - -class Controller(_base.Controller): - _KeyCode = KeyCode - _Key = Key - - def __init__(self, *args, **kwargs): - super(Controller, self).__init__(*args, **kwargs) - - def _handle(self, key, is_press): - try: - SendInput( - 1, - ctypes.byref(INPUT( - type=INPUT.KEYBOARD, - value=INPUT_union( - ki=KEYBDINPUT(**key._parameters(is_press))))), - ctypes.sizeof(INPUT)) - except ValueError: - # If key._parameters raises ValueError, the key is a unicode - # characters outsice of the range of a single UTF-16 value, and we - # must break it up into its surrogates - byte_data = bytearray(key.char.encode('utf-16le')) - surrogates = [ - byte_data[i] | (byte_data[i + 1] << 8) - for i in range(0, len(byte_data), 2)] - - state_flags = KEYBDINPUT.UNICODE \ - | (KEYBDINPUT.KEYUP if not is_press else 0) - - SendInput( - len(surrogates), - (INPUT * len(surrogates))(*( - INPUT( - INPUT.KEYBOARD, - INPUT_union( - ki=KEYBDINPUT( - dwFlags=state_flags, - wScan=scan))) - for scan in surrogates)), - ctypes.sizeof(INPUT)) - - -class Listener(ListenerMixin, _base.Listener): - #: The Windows hook ID for low level keyboard events, ``WH_KEYBOARD_LL`` - _EVENTS = 13 - - _WM_INPUTLANGCHANGE = 0x0051 - _WM_KEYDOWN = 0x0100 - _WM_KEYUP = 0x0101 - _WM_SYSKEYDOWN = 0x0104 - _WM_SYSKEYUP = 0x0105 - - # A bit flag attached to messages indicating that the payload is an actual - # UTF-16 character code - _UTF16_FLAG = 0x1000 - - # A bit flag attached to messages indicating that the event was injected - _INJECTED_FLAG = 0x2000 - - # A special virtual key code designating unicode characters - _VK_PACKET = 0xE7 - - #: The messages that correspond to a key press - _PRESS_MESSAGES = (_WM_KEYDOWN, _WM_SYSKEYDOWN) - - #: The messages that correspond to a key release - _RELEASE_MESSAGES = (_WM_KEYUP, _WM_SYSKEYUP) - - #: Additional window messages to propagate to the subclass handler. - _WM_NOTIFICATIONS = ( - _WM_INPUTLANGCHANGE, - ) - - #: A mapping from keysym to special key - _SPECIAL_KEYS = { - key.value.vk: key - for key in Key} - - _HANDLED_EXCEPTIONS = ( - SystemHook.SuppressException,) - - class _KBDLLHOOKSTRUCT(ctypes.Structure): - """Contains information about a mouse event passed to a - ``WH_KEYBOARD_LL`` hook procedure, ``LowLevelKeyboardProc``. - """ - LLKHF_INJECTED = 0x00000010 - LLKHF_LOWER_IL_INJECTED = 0x00000002 - _fields_ = [ - ('vkCode', wintypes.DWORD), - ('scanCode', wintypes.DWORD), - ('flags', wintypes.DWORD), - ('time', wintypes.DWORD), - ('dwExtraInfo', ctypes.c_void_p)] - - #: A pointer to a :class:`KBDLLHOOKSTRUCT` - _LPKBDLLHOOKSTRUCT = ctypes.POINTER(_KBDLLHOOKSTRUCT) - - def __init__(self, *args, **kwargs): - super(Listener, self).__init__(*args, **kwargs) - self._translator = KeyTranslator() - self._event_filter = self._options.get( - 'event_filter', - lambda msg, data: True) - - def _convert(self, code, msg, lpdata): - if code != SystemHook.HC_ACTION: - return - - data = ctypes.cast(lpdata, self._LPKBDLLHOOKSTRUCT).contents - is_packet = data.vkCode == self._VK_PACKET - injected = (data.flags & (0 - | self._KBDLLHOOKSTRUCT.LLKHF_INJECTED - | self._KBDLLHOOKSTRUCT.LLKHF_LOWER_IL_INJECTED)) != 0 - message = (msg - | (self._UTF16_FLAG if is_packet else 0) - | (self._INJECTED_FLAG if injected else 0)) - - # Suppress further propagation of the event if it is filtered - if self._event_filter(msg, data) is False: - return None - elif is_packet: - return (message, data.scanCode) - else: - return (message, data.vkCode) - - @AbstractListener._emitter - def _process(self, wparam, lparam): - msg = wparam - vk = lparam - - # If the key has the UTF-16 flag, we treat it as a unicode character, - # otherwise convert the event to a KeyCode; this may fail, and in that - # case we pass None - is_utf16 = msg & self._UTF16_FLAG - injected = bool(msg & self._INJECTED_FLAG) - message = msg & ~(self._UTF16_FLAG | self._INJECTED_FLAG) - if is_utf16: - scan = vk - key = KeyCode.from_char(six.unichr(scan)) - else: - try: - key = self._event_to_key(msg, vk) - except OSError: - key = None - - if message in self._PRESS_MESSAGES: - self.on_press(key, injected) - - elif message in self._RELEASE_MESSAGES: - self.on_release(key, injected) - - # pylint: disable=R0201 - @contextlib.contextmanager - def _receive(self): - """An empty context manager; we do not need to fake keyboard events. - """ - yield - # pylint: enable=R0201 - - def _on_notification(self, code, wparam, lparam): - """Receives ``WM_INPUTLANGCHANGE`` and updates the cached layout. - """ - if code == self._WM_INPUTLANGCHANGE: - self._translator.update_layout() - - def _event_to_key(self, msg, vk): - """Converts an :class:`_KBDLLHOOKSTRUCT` to a :class:`KeyCode`. - - :param msg: The message received. - - :param vk: The virtual key code to convert. - - :return: a :class:`pynput.keyboard.KeyCode` - - :raises OSError: if the message and data could not be converted - """ - # If the virtual key code corresponds to a Key value, we prefer that - if vk in self._SPECIAL_KEYS: - return self._SPECIAL_KEYS[vk] - else: - return KeyCode(**self._translate( - vk, - msg in self._PRESS_MESSAGES)) - - def _translate(self, vk, is_press): - """Translates a virtual key code to a parameter list passable to - :class:`pynput.keyboard.KeyCode`. - - :param int vk: The virtual key code. - - :param bool is_press: Whether this is a press event. - - :return: a parameter list to the :class:`pynput.keyboard.KeyCode` - constructor - """ - return self._translator(vk, is_press) - - def canonical(self, key): - # If the key has a scan code, and we can find the character for it, - # return that, otherwise call the super class - scan = getattr(key, '_scan', None) - if scan is not None: - char = self._translator.char_from_scan(scan) - if char is not None: - return KeyCode.from_char(char) - - return super(Listener, self).canonical(key) diff --git a/PortablePython/Lib/site-packages/pynput/keyboard/_xorg.py b/PortablePython/Lib/site-packages/pynput/keyboard/_xorg.py deleted file mode 100644 index 7011a5a..0000000 --- a/PortablePython/Lib/site-packages/pynput/keyboard/_xorg.py +++ /dev/null @@ -1,667 +0,0 @@ -# coding=utf-8 -# pynput -# Copyright (C) 2015-2024 Moses Palmér -# -# This program 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 3 of the License, or (at your option) any -# later version. -# -# This program 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 program. If not, see . -""" -The keyboard implementation for *Xorg*. -""" - -# pylint: disable=C0111 -# The documentation is extracted from the base classes - -# pylint: disable=R0903 -# We implement stubs - -# pylint: disable=W0611 -try: - import pynput._util.xorg -except Exception as e: - raise ImportError('failed to acquire X connection: {}'.format(str(e)), e) -# pylint: enable=W0611 - -import enum -import threading - -import Xlib.display -import Xlib.ext -import Xlib.ext.xtest -import Xlib.X -import Xlib.XK -import Xlib.protocol -import Xlib.keysymdef.xkb - -from pynput._util import NotifierMixin -from pynput._util.xorg import ( - alt_mask, - alt_gr_mask, - char_to_keysym, - display_manager, - index_to_shift, - keyboard_mapping, - ListenerMixin, - numlock_mask, - shift_to_index, - symbol_to_keysym) -from pynput._util.xorg_keysyms import ( - CHARS, - DEAD_KEYS, - KEYPAD_KEYS, - KEYSYMS, - SYMBOLS) -from . import _base - - -class KeyCode(_base.KeyCode): - _PLATFORM_EXTENSIONS = ( - # The symbol name for this key - '_symbol', - ) - - # Be explicit about fields - _symbol = None - - @classmethod - def _from_symbol(cls, symbol, **kwargs): - """Creates a key from a symbol. - - :param str symbol: The symbol name. - - :return: a key code - """ - # First try simple translation - keysym = Xlib.XK.string_to_keysym(symbol) - if keysym: - return cls.from_vk(keysym, _symbol=symbol, **kwargs) - - # If that fails, try checking a module attribute of Xlib.keysymdef.xkb - if not keysym: - # pylint: disable=W0702; we want to ignore errors - try: - symbol = 'XK_' + symbol - return cls.from_vk( - getattr(Xlib.keysymdef.xkb, symbol, 0), - _symbol=symbol, - **kwargs) - except: - return cls.from_vk( - SYMBOLS.get(symbol, (0,))[0], - _symbol=symbol, - **kwargs) - # pylint: enable=W0702 - - @classmethod - def _from_media(cls, name, **kwargs): - """Creates a media key from a partial name. - - :param str name: The name. The actual symbol name will be this string - with ``'XF86_Audio'`` prepended. - - :return: a key code - """ - return cls._from_symbol('XF86_Audio' + name, **kwargs) - - -# pylint: disable=W0212 -class Key(enum.Enum): - # Default keys - alt = KeyCode._from_symbol('Alt_L') - alt_l = KeyCode._from_symbol('Alt_L') - alt_r = KeyCode._from_symbol('Alt_R') - alt_gr = KeyCode._from_symbol('Mode_switch') - backspace = KeyCode._from_symbol('BackSpace') - caps_lock = KeyCode._from_symbol('Caps_Lock') - cmd = KeyCode._from_symbol('Super_L') - cmd_l = KeyCode._from_symbol('Super_L') - cmd_r = KeyCode._from_symbol('Super_R') - ctrl = KeyCode._from_symbol('Control_L') - ctrl_l = KeyCode._from_symbol('Control_L') - ctrl_r = KeyCode._from_symbol('Control_R') - delete = KeyCode._from_symbol('Delete') - down = KeyCode._from_symbol('Down') - end = KeyCode._from_symbol('End') - enter = KeyCode._from_symbol('Return') - esc = KeyCode._from_symbol('Escape') - f1 = KeyCode._from_symbol('F1') - f2 = KeyCode._from_symbol('F2') - f3 = KeyCode._from_symbol('F3') - f4 = KeyCode._from_symbol('F4') - f5 = KeyCode._from_symbol('F5') - f6 = KeyCode._from_symbol('F6') - f7 = KeyCode._from_symbol('F7') - f8 = KeyCode._from_symbol('F8') - f9 = KeyCode._from_symbol('F9') - f10 = KeyCode._from_symbol('F10') - f11 = KeyCode._from_symbol('F11') - f12 = KeyCode._from_symbol('F12') - f13 = KeyCode._from_symbol('F13') - f14 = KeyCode._from_symbol('F14') - f15 = KeyCode._from_symbol('F15') - f16 = KeyCode._from_symbol('F16') - f17 = KeyCode._from_symbol('F17') - f18 = KeyCode._from_symbol('F18') - f19 = KeyCode._from_symbol('F19') - f20 = KeyCode._from_symbol('F20') - home = KeyCode._from_symbol('Home') - left = KeyCode._from_symbol('Left') - page_down = KeyCode._from_symbol('Page_Down') - page_up = KeyCode._from_symbol('Page_Up') - right = KeyCode._from_symbol('Right') - shift = KeyCode._from_symbol('Shift_L') - shift_l = KeyCode._from_symbol('Shift_L') - shift_r = KeyCode._from_symbol('Shift_R') - space = KeyCode._from_symbol('space', char=' ') - tab = KeyCode._from_symbol('Tab') - up = KeyCode._from_symbol('Up') - - media_play_pause = KeyCode._from_media('Play') - media_volume_mute = KeyCode._from_media('Mute') - media_volume_down = KeyCode._from_media('LowerVolume') - media_volume_up = KeyCode._from_media('RaiseVolume') - media_previous = KeyCode._from_media('Prev') - media_next = KeyCode._from_media('Next') - - insert = KeyCode._from_symbol('Insert') - menu = KeyCode._from_symbol('Menu') - num_lock = KeyCode._from_symbol('Num_Lock') - pause = KeyCode._from_symbol('Pause') - print_screen = KeyCode._from_symbol('Print') - scroll_lock = KeyCode._from_symbol('Scroll_Lock') -# pylint: enable=W0212 - - -class Controller(NotifierMixin, _base.Controller): - _KeyCode = KeyCode - _Key = Key - - #: The shift mask for :attr:`Key.ctrl` - CTRL_MASK = Xlib.X.ControlMask - - #: The shift mask for :attr:`Key.shift` - SHIFT_MASK = Xlib.X.ShiftMask - - def __init__(self, *args, **kwargs): - super(Controller, self).__init__(*args, **kwargs) - self._display = Xlib.display.Display() - self._keyboard_mapping = None - self._borrows = {} - self._borrow_lock = threading.RLock() - - # pylint: disable=C0103; this is treated as a class scope constant, but - # we cannot set it in the class scope, as it requires a Display instance - self.ALT_MASK = alt_mask(self._display) - self.ALT_GR_MASK = alt_gr_mask(self._display) - # pylint: enable=C0103 - - def __del__(self): - if hasattr(self, '_display'): - self._display.close() - - @property - def keyboard_mapping(self): - """A mapping from *keysyms* to *key codes*. - - Each value is the tuple ``(key_code, shift_state)``. By sending an - event with the specified *key code* and shift state, the specified - *keysym* will be touched. - """ - if not self._keyboard_mapping: - self._update_keyboard_mapping() - return self._keyboard_mapping - - def _handle(self, key, is_press): - """Resolves a key identifier and sends a keyboard event. - - :param int key: The key to handle. - :param bool is_press: Whether this is a press. - """ - event = Xlib.display.event.KeyPress if is_press \ - else Xlib.display.event.KeyRelease - keysym = self._keysym(key) - - # Make sure to verify that the key was resolved - if keysym is None: - raise self.InvalidKeyException(key) - - # If the key has a virtual key code, use that immediately with - # fake_input; fake input,being an X server extension, has access to - # more internal state that we do - if key.vk is not None: - with display_manager(self._display) as dm: - Xlib.ext.xtest.fake_input( - dm, - Xlib.X.KeyPress if is_press else Xlib.X.KeyRelease, - dm.keysym_to_keycode(key.vk)) - - # Otherwise use XSendEvent; we need to use this in the general case to - # work around problems with keyboard layouts - else: - try: - keycode, shift_state = self.keyboard_mapping[keysym] - self._send_key(event, keycode, shift_state) - - except KeyError: - with self._borrow_lock: - keycode, index, count = self._borrows[keysym] - self._send_key( - event, - keycode, - index_to_shift(self._display, index)) - count += 1 if is_press else -1 - self._borrows[keysym] = (keycode, index, count) - - # Notify any running listeners - self._emit('_on_fake_event', key, is_press) - - def _keysym(self, key): - """Converts a key to a *keysym*. - - :param KeyCode key: The key code to convert. - """ - return self._resolve_dead(key) if key.is_dead else None \ - or self._resolve_special(key) \ - or self._resolve_normal(key) \ - or self._resolve_borrowed(key) \ - or self._resolve_borrowing(key) - - def _send_key(self, event, keycode, shift_state): - """Sends a single keyboard event. - - :param event: The *X* keyboard event. - - :param int keycode: The calculated keycode. - - :param int shift_state: The shift state. The actual value used is - :attr:`shift_state` or'd with this value. - """ - with display_manager(self._display) as dm, self.modifiers as modifiers: - # Under certain cimcumstances, such as when running under Xephyr, - # the value returned by dm.get_input_focus is an int - window = dm.get_input_focus().focus - send_event = getattr( - window, - 'send_event', - lambda event: dm.send_event(window, event)) - send_event(event( - detail=keycode, - state=shift_state | self._shift_mask(modifiers), - time=0, - root=dm.screen().root, - window=window, - same_screen=0, - child=Xlib.X.NONE, - root_x=0, root_y=0, event_x=0, event_y=0)) - - def _resolve_dead(self, key): - """Tries to resolve a dead key. - - :param str identifier: The identifier to resolve. - """ - # pylint: disable=W0702; we want to ignore errors - try: - keysym, _ = SYMBOLS[CHARS[key.combining]] - except: - return None - # pylint: enable=W0702 - - if keysym not in self.keyboard_mapping: - return None - - return keysym - - def _resolve_special(self, key): - """Tries to resolve a special key. - - A special key has the :attr:`~KeyCode.vk` attribute set. - - :param KeyCode key: The key to resolve. - """ - if not key.vk: - return None - - return key.vk - - def _resolve_normal(self, key): - """Tries to resolve a normal key. - - A normal key exists on the keyboard, and is typed by pressing - and releasing a simple key, possibly in combination with a modifier. - - :param KeyCode key: The key to resolve. - """ - keysym = self._key_to_keysym(key) - if keysym is None: - return None - - if keysym not in self.keyboard_mapping: - return None - - return keysym - - def _resolve_borrowed(self, key): - """Tries to resolve a key by looking up the already borrowed *keysyms*. - - A borrowed *keysym* does not exist on the keyboard, but has been - temporarily added to the layout. - - :param KeyCode key: The key to resolve. - """ - keysym = self._key_to_keysym(key) - if keysym is None: - return None - - with self._borrow_lock: - if keysym not in self._borrows: - return None - - return keysym - - def _resolve_borrowing(self, key): - """Tries to resolve a key by modifying the layout temporarily. - - A borrowed *keysym* does not exist on the keyboard, but is temporarily - added to the layout. - - :param KeyCode key: The key to resolve. - """ - keysym = self._key_to_keysym(key) - if keysym is None: - return None - - mapping = self._display.get_keyboard_mapping(8, 255 - 8) - - def i2kc(index): - return index + 8 - - def kc2i(keycode): - return keycode - 8 - - #: Finds a keycode and index by looking at already used keycodes - def reuse(): - for _, (keycode, _, _) in self._borrows.items(): - keycodes = mapping[kc2i(keycode)] - - # Only the first four items are addressable by X - for index in range(4): - if not keycodes[index]: - return keycode, index - - #: Finds a keycode and index by using a new keycode - def borrow(): - for i, keycodes in enumerate(mapping): - if not any(keycodes): - return i2kc(i), 0 - - #: Finds a keycode and index by reusing an old, unused one - def overwrite(): - for keysym, (keycode, index, count) in self._borrows.items(): - if count < 1: - del self._borrows[keysym] - return keycode, index - - #: Registers a keycode for a specific key and modifier state - def register(dm, keycode, index): - i = kc2i(keycode) - - # Check for use of empty mapping with a character that has upper - # and lower forms - lower = key.char.lower() - upper = key.char.upper() - if lower != upper and len(lower) == 1 and len(upper) == 1 and all( - m == Xlib.XK.NoSymbol - for m in mapping[i]): - lower = self._key_to_keysym(KeyCode.from_char(lower)) - upper = self._key_to_keysym(KeyCode.from_char(upper)) - if lower: - mapping[i][0] = lower - self._borrows[lower] = (keycode, 0, 0) - if upper: - mapping[i][1] = upper - self._borrows[upper] = (keycode, 1, 0) - else: - mapping[i][index] = keysym - self._borrows[keysym] = (keycode, index, 0) - dm.change_keyboard_mapping(keycode, mapping[i:i + 1]) - - try: - with display_manager(self._display) as dm, self._borrow_lock as _: - # First try an already used keycode, then try a new one, and - # fall back on reusing one that is not currently pressed - register(dm, *( - reuse() or - borrow() or - overwrite())) - return keysym - - except TypeError: - return None - - def _key_to_keysym(self, key): - """Converts a character key code to a *keysym*. - - :param KeyCode key: The key code. - - :return: a keysym if found - :rtype: int or None - """ - # If the key code already has a VK, simply return it - if key.vk is not None: - return key.vk - - # If the character has no associated symbol, we try to map the - # character to a keysym - symbol = CHARS.get(key.char, None) - if symbol is None: - return char_to_keysym(key.char) - - # Otherwise we attempt to convert the symbol to a keysym - # pylint: disable=W0702; we want to ignore errors - try: - return symbol_to_keysym(symbol) - except: - try: - return SYMBOLS[symbol][0] - except: - return None - # pylint: enable=W0702 - - def _shift_mask(self, modifiers): - """The *X* modifier mask to apply for a set of modifiers. - - :param set modifiers: A set of active modifiers for which to get the - shift mask. - """ - return ( - 0 - | (self.ALT_MASK - if Key.alt in modifiers else 0) - - | (self.ALT_GR_MASK - if Key.alt_gr in modifiers else 0) - - | (self.CTRL_MASK - if Key.ctrl in modifiers else 0) - - | (self.SHIFT_MASK - if Key.shift in modifiers else 0)) - - def _update_keyboard_mapping(self): - """Updates the keyboard mapping. - """ - with display_manager(self._display) as dm: - self._keyboard_mapping = keyboard_mapping(dm) - - -@Controller._receiver -class Listener(ListenerMixin, _base.Listener): - _EVENTS = ( - Xlib.X.KeyPress, - Xlib.X.KeyRelease) - - #: A mapping from keysym to special key - _SPECIAL_KEYS = { - key.value.vk: key - for key in Key} - - #: A mapping from numeric keypad keys to keys - _KEYPAD_KEYS = { - KEYPAD_KEYS['KP_0']: KeyCode.from_char('0'), - KEYPAD_KEYS['KP_1']: KeyCode.from_char('1'), - KEYPAD_KEYS['KP_2']: KeyCode.from_char('2'), - KEYPAD_KEYS['KP_3']: KeyCode.from_char('3'), - KEYPAD_KEYS['KP_4']: KeyCode.from_char('4'), - KEYPAD_KEYS['KP_5']: KeyCode.from_char('5'), - KEYPAD_KEYS['KP_6']: KeyCode.from_char('6'), - KEYPAD_KEYS['KP_7']: KeyCode.from_char('7'), - KEYPAD_KEYS['KP_8']: KeyCode.from_char('8'), - KEYPAD_KEYS['KP_9']: KeyCode.from_char('9'), - KEYPAD_KEYS['KP_Add']: KeyCode.from_char('+'), - KEYPAD_KEYS['KP_Decimal']: KeyCode.from_char(','), - KEYPAD_KEYS['KP_Delete']: Key.delete, - KEYPAD_KEYS['KP_Divide']: KeyCode.from_char('/'), - KEYPAD_KEYS['KP_Down']: Key.down, - KEYPAD_KEYS['KP_End']: Key.end, - KEYPAD_KEYS['KP_Enter']: Key.enter, - KEYPAD_KEYS['KP_Equal']: KeyCode.from_char('='), - KEYPAD_KEYS['KP_F1']: Key.f1, - KEYPAD_KEYS['KP_F2']: Key.f2, - KEYPAD_KEYS['KP_F3']: Key.f3, - KEYPAD_KEYS['KP_F4']: Key.f4, - KEYPAD_KEYS['KP_Home']: Key.home, - KEYPAD_KEYS['KP_Insert']: Key.insert, - KEYPAD_KEYS['KP_Left']: Key.left, - KEYPAD_KEYS['KP_Multiply']: KeyCode.from_char('*'), - KEYPAD_KEYS['KP_Page_Down']: Key.page_down, - KEYPAD_KEYS['KP_Page_Up']: Key.page_up, - KEYPAD_KEYS['KP_Right']: Key.right, - KEYPAD_KEYS['KP_Space']: Key.space, - KEYPAD_KEYS['KP_Subtract']: KeyCode.from_char('-'), - KEYPAD_KEYS['KP_Tab']: Key.tab, - KEYPAD_KEYS['KP_Up']: Key.up} - - def __init__(self, *args, **kwargs): - super(Listener, self).__init__(*args, **kwargs) - self._keyboard_mapping = None - - def _run(self): - with self._receive(): - super(Listener, self)._run() - - def _initialize(self, display): - # Get the keyboard mapping to be able to translate event details to - # key codes - min_keycode = display.display.info.min_keycode - keycode_count = display.display.info.max_keycode - min_keycode + 1 - self._keyboard_mapping = display.get_keyboard_mapping( - min_keycode, keycode_count) - - def _handle_message(self, display, event, injected): - # Convert the event to a KeyCode; this may fail, and in that case we - # pass None - try: - key = self._event_to_key(display, event) - except IndexError: - key = None - - if event.type == Xlib.X.KeyPress: - self.on_press(key, injected) - - elif event.type == Xlib.X.KeyRelease: - self.on_release(key, injected) - - def _suppress_start(self, display): - display.screen().root.grab_keyboard( - self._event_mask, Xlib.X.GrabModeAsync, Xlib.X.GrabModeAsync, - Xlib.X.CurrentTime) - - def _suppress_stop(self, display): - display.ungrab_keyboard(Xlib.X.CurrentTime) - - def _on_fake_event(self, key, is_press): - """The handler for fake press events sent by the controllers. - - :param KeyCode key: The key pressed. - - :param bool is_press: Whether this is a press event. - """ - (self.on_press if is_press else self.on_release)( - self._SPECIAL_KEYS.get(key.vk, key), True) - - def _keycode_to_keysym(self, display, keycode, index): - """Converts a keycode and shift state index to a keysym. - - This method uses a simplified version of the *X* convention to locate - the correct keysym in the display table: since this method is only used - to locate special keys, alphanumeric keys are not treated specially. - - :param display: The current *X* display. - - :param keycode: The keycode. - - :param index: The shift state index. - - :return: a keysym - """ - keysym = display.keycode_to_keysym(keycode, index) - if keysym: - return keysym - elif index & 0x2: - return self._keycode_to_keysym(display, keycode, index & ~0x2) - elif index & 0x1: - return self._keycode_to_keysym(display, keycode, index & ~0x1) - else: - return 0 - - def _event_to_key(self, display, event): - """Converts an *X* event to a :class:`KeyCode`. - - :param display: The current *X* display. - - :param event: The event to convert. - - :return: a :class:`pynput.keyboard.KeyCode` - - :raises IndexError: if the key code is invalid - """ - keycode = event.detail - index = shift_to_index(display, event.state) - - # First try special keys... - keysym = self._keycode_to_keysym(display, keycode, index) - if keysym in self._SPECIAL_KEYS: - return self._SPECIAL_KEYS[keysym] - elif keysym in self._KEYPAD_KEYS: - # We must recalculate the index if numlock is active; index 1 is the - # one to use - try: - return self._KEYPAD_KEYS[ - self._keycode_to_keysym( - display, - keycode, - bool(event.state & numlock_mask(display)))] - except KeyError: - # Since we recalculated the key, this may happen - pass - - # ...then try characters... - name = KEYSYMS.get(keysym, None) - if name is not None and name in SYMBOLS: - char = SYMBOLS[name][1].upper() if index & 1 else SYMBOLS[name][1] - if char in DEAD_KEYS: - return KeyCode.from_dead(DEAD_KEYS[char], vk=keysym) - else: - return KeyCode.from_char(char, vk=keysym) - - # ...and fall back on a virtual key code - return KeyCode.from_vk(keysym) diff --git a/PortablePython/Lib/site-packages/pynput/mouse/__init__.py b/PortablePython/Lib/site-packages/pynput/mouse/__init__.py deleted file mode 100644 index 3456629..0000000 --- a/PortablePython/Lib/site-packages/pynput/mouse/__init__.py +++ /dev/null @@ -1,107 +0,0 @@ -# coding=utf-8 -# pynput -# Copyright (C) 2015-2024 Moses Palmér -# -# This program 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 3 of the License, or (at your option) any -# later version. -# -# This program 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 program. If not, see . -""" -The module containing mouse classes. - -See the documentation for more information. -""" - -# pylint: disable=C0103 -# Button, Controller and Listener are not constants - -from pynput._util import backend, Events - - -backend = backend(__name__) -Button = backend.Button -Controller = backend.Controller -Listener = backend.Listener -del backend - - -class Events(Events): - """A mouse event listener supporting synchronous iteration over the events. - - Possible events are: - - :class:`Events.Move` - The mouse was moved. - - :class:`Events.Click` - A mouse button was pressed or released. - - :class:`Events.Scroll` - The device was scrolled. - """ - _Listener = Listener - - class Move(Events.Event): - """A move event. - """ - def __init__(self, x, y, injected): - #: The X screen coordinate. - self.x = x - - #: The Y screen coordinate. - self.y = y - - #: Whether this event is synthetic. - self.injected = injected - - class Click(Events.Event): - """A click event. - """ - def __init__(self, x, y, button, pressed, injected): - #: The X screen coordinate. - self.x = x - - #: The Y screen coordinate. - self.y = y - - #: The button. - self.button = button - - #: Whether the button was pressed. - self.pressed = pressed - - #: Whether this event is synthetic. - self.injected = injected - - class Scroll(Events.Event): - """A scroll event. - """ - def __init__(self, x, y, dx, dy, injected): - #: The X screen coordinate. - self.x = x - - #: The Y screen coordinate. - self.y = y - - #: The number of horisontal steps. - self.dx = dx - - #: The number of vertical steps. - self.dy = dy - - #: Whether this event is synthetic. - self.injected = injected - - def __init__(self): - super(Events, self).__init__( - on_move=self.Move, - on_click=self.Click, - on_scroll=self.Scroll) diff --git a/PortablePython/Lib/site-packages/pynput/mouse/_base.py b/PortablePython/Lib/site-packages/pynput/mouse/_base.py deleted file mode 100644 index 186a7d7..0000000 --- a/PortablePython/Lib/site-packages/pynput/mouse/_base.py +++ /dev/null @@ -1,281 +0,0 @@ -# coding=utf-8 -# pynput -# Copyright (C) 2015-2024 Moses Palmér -# -# This program 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 3 of the License, or (at your option) any -# later version. -# -# This program 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 program. If not, see . -""" -This module contains the base implementation. - -The actual interface to mouse classes is defined here, but the implementation -is located in a platform dependent module. -""" - -# pylint: disable=R0903 -# We implement stubs - -import enum - -from pynput._util import AbstractListener, prefix -from pynput import _logger - - -class Button(enum.Enum): - """The various buttons. - - The actual values for these items differ between platforms. Some - platforms may have additional buttons, but these are guaranteed to be - present everywhere. - """ - #: An unknown button was pressed - unknown = 0 - - #: The left button - left = 1 - - #: The middle button - middle = 2 - - #: The right button - right = 3 - - -class Controller(object): - """A controller for sending virtual mouse events to the system. - """ - def __init__(self): - self._log = _logger(self.__class__) - - @property - def position(self): - """The current position of the mouse pointer. - - This is the tuple ``(x, y)``, and setting it will move the pointer. - """ - return self._position_get() - - @position.setter - def position(self, pos): - self._position_set(pos) - - def scroll(self, dx, dy): - """Sends scroll events. - - :param int dx: The horizontal scroll. The units of scrolling is - undefined. - - :param int dy: The vertical scroll. The units of scrolling is - undefined. - - :raises ValueError: if the values are invalid, for example out of - bounds - """ - self._scroll(dx, dy) - - def press(self, button): - """Emits a button press event at the current position. - - :param Button button: The button to press. - """ - self._press(button) - - def release(self, button): - """Emits a button release event at the current position. - - :param Button button: The button to release. - """ - self._release(button) - - def move(self, dx, dy): - """Moves the mouse pointer a number of pixels from its current - position. - - :param int dx: The horizontal offset. - - :param int dy: The vertical offset. - - :raises ValueError: if the values are invalid, for example out of - bounds - """ - self.position = tuple(sum(i) for i in zip(self.position, (dx, dy))) - - def click(self, button, count=1): - """Emits a button click event at the current position. - - The default implementation sends a series of press and release events. - - :param Button button: The button to click. - - :param int count: The number of clicks to send. - """ - with self as controller: - for _ in range(count): - controller.press(button) - controller.release(button) - - def __enter__(self): - """Begins a series of clicks. - - In the default :meth:`click` implementation, the return value of this - method is used for the calls to :meth:`press` and :meth:`release` - instead of ``self``. - - The default implementation is a no-op. - """ - return self - - def __exit__(self, exc_type, value, traceback): - """Ends a series of clicks. - """ - pass - - def _position_get(self): - """The implementation of the getter for :attr:`position`. - - This is a platform dependent implementation. - """ - raise NotImplementedError() - - def _position_set(self, pos): - """The implementation of the setter for :attr:`position`. - - This is a platform dependent implementation. - """ - raise NotImplementedError() - - def _scroll(self, dx, dy): - """The implementation of the :meth:`scroll` method. - - This is a platform dependent implementation. - """ - raise NotImplementedError() - - def _press(self, button): - """The implementation of the :meth:`press` method. - - This is a platform dependent implementation. - """ - raise NotImplementedError() - - def _release(self, button): - """The implementation of the :meth:`release` method. - - This is a platform dependent implementation. - """ - raise NotImplementedError() - - -# pylint: disable=W0223; This is also an abstract class -class Listener(AbstractListener): - """A listener for mouse events. - - Instances of this class can be used as context managers. This is equivalent - to the following code:: - - listener.start() - try: - listener.wait() - with_statements() - finally: - listener.stop() - - This class inherits from :class:`threading.Thread` and supports all its - methods. It will set :attr:`daemon` to ``True`` when created. - - All callback arguments are optional; a callback may take less arguments - than actually passed, but not more, unless they are optional. - - :param callable on_move: The callback to call when mouse move events occur. - - It will be called with the arguments ``(x, y, injected)``, where ``(x, - y)`` is the new pointer position and ``injected`` whether the event was - injected and thus not generated by an actual input device. If this - callback raises :class:`StopException` or returns ``False``, the - listener is stopped. - - Please note that not all backends support ``injected`` and will always - set it to ``False``. - - :param callable on_click: The callback to call when a mouse button is - clicked. - - It will be called with the arguments ``(x, y, button, pressed, - injected)``, where ``(x, y)`` is the new pointer position, ``button`` - is one of the :class:`Button`, ``pressed`` is whether the button was - pressed and ``injected`` whether the event was injected and thus not - generated by an actual input device. - - If this callback raises :class:`StopException` or returns ``False``, - the listener is stopped. - - Please note that not all backends support ``injected`` and will always - set it to ``False``. - - :param callable on_scroll: The callback to call when mouse scroll - events occur. - - It will be called with the arguments ``(x, y, dx, dy, injected)``, - where ``(x, y)`` is the new pointer position, and ``(dx, dy)`` is the - scroll vector and ``injected`` whether the event was injected and thus - not generated by an actual input device. - - If this callback raises :class:`StopException` or returns ``False``, - the listener is stopped. - - Please note that not all backends support ``injected`` and will always - set it to ``False``. - - :param bool suppress: Whether to suppress events. Setting this to ``True`` - will prevent the input events from being passed to the rest of the - system. - - :param kwargs: Any non-standard platform dependent options. These should be - prefixed with the platform name thus: ``darwin_``, ``xorg_`` or - ``win32_``. - - Supported values are: - - ``darwin_intercept`` - A callable taking the arguments ``(event_type, event)``, where - ``event_type`` is any mouse related event type constant, and - ``event`` is a ``CGEventRef``. - - This callable can freely modify the event using functions like - ``Quartz.CGEventSetIntegerValueField``. If this callable does not - return the event, the event is suppressed system wide. - - ``win32_event_filter`` - A callable taking the arguments ``(msg, data)``, where ``msg`` is - the current message, and ``data`` associated data as a - `MSLLHOOKSTRUCT `_. - - If this callback returns ``False``, the event will not - be propagated to the listener callback. - - If ``self.suppress_event()`` is called, the event is suppressed - system wide. - """ - def __init__(self, on_move=None, on_click=None, on_scroll=None, - suppress=False, **kwargs): - self._log = _logger(self.__class__) - option_prefix = prefix(Listener, self.__class__) - self._options = { - key[len(option_prefix):]: value - for key, value in kwargs.items() - if key.startswith(option_prefix)} - super(Listener, self).__init__( - on_move=self._wrap(on_move, 3), - on_click=self._wrap(on_click, 5), - on_scroll=self._wrap(on_scroll, 5), - suppress=suppress) -# pylint: enable=W0223 diff --git a/PortablePython/Lib/site-packages/pynput/mouse/_darwin.py b/PortablePython/Lib/site-packages/pynput/mouse/_darwin.py deleted file mode 100644 index b5dbe3c..0000000 --- a/PortablePython/Lib/site-packages/pynput/mouse/_darwin.py +++ /dev/null @@ -1,212 +0,0 @@ -# coding=utf-8 -# pynput -# Copyright (C) 2015-2024 Moses Palmér -# -# This program 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 3 of the License, or (at your option) any -# later version. -# -# This program 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 program. If not, see . -""" -The mouse implementation for *macOS*. -""" - -# pylint: disable=C0111 -# The documentation is extracted from the base classes - -# pylint: disable=R0903 -# We implement stubs - -import enum -import Quartz - -from AppKit import NSEvent - -from pynput._util.darwin import ( - ListenerMixin) -from . import _base - - -def _button_value(base_name, mouse_button): - """Generates the value tuple for a :class:`Button` value. - - :param str base_name: The base name for the button. This should be a string - like ``'kCGEventLeftMouse'``. - - :param int mouse_button: The mouse button ID. - - :return: a value tuple - """ - return ( - tuple( - getattr(Quartz, '%sMouse%s' % (base_name, name)) - for name in ('Down', 'Up', 'Dragged')), - mouse_button) - - -class Button(enum.Enum): - """The various buttons. - """ - unknown = None - left = _button_value('kCGEventLeft', 0) - middle = _button_value('kCGEventOther', 2) - right = _button_value('kCGEventRight', 1) - - -class Controller(_base.Controller): - #: The scroll speed - _SCROLL_SPEED = 10 - - def __init__(self, *args, **kwargs): - super(Controller, self).__init__(*args, **kwargs) - self._click = None - self._drag_button = None - - def _position_get(self): - pos = NSEvent.mouseLocation() - - return pos.x, Quartz.CGDisplayPixelsHigh(0) - pos.y - - def _position_set(self, pos): - try: - (_, _, mouse_type), mouse_button = self._drag_button.value - except AttributeError: - mouse_type = Quartz.kCGEventMouseMoved - mouse_button = 0 - - Quartz.CGEventPost( - Quartz.kCGHIDEventTap, - Quartz.CGEventCreateMouseEvent( - None, - mouse_type, - pos, - mouse_button)) - - def _scroll(self, dx, dy): - dx = int(dx) - dy = int(dy) - - Quartz.CGEventPost( - Quartz.kCGHIDEventTap, - Quartz.CGEventCreateScrollWheelEvent( - None, - Quartz.kCGScrollEventUnitPixel, - 2, - dy * self._SCROLL_SPEED, - dx * self._SCROLL_SPEED)) - - def _press(self, button): - (press, _, _), mouse_button = button.value - event = Quartz.CGEventCreateMouseEvent( - None, - press, - self.position, - mouse_button) - - # If we are performing a click, we need to set this state flag - if self._click is not None: - self._click += 1 - Quartz.CGEventSetIntegerValueField( - event, - Quartz.kCGMouseEventClickState, - self._click) - - Quartz.CGEventPost(Quartz.kCGHIDEventTap, event) - - # Store the button to enable dragging - self._drag_button = button - - def _release(self, button): - (_, release, _), mouse_button = button.value - event = Quartz.CGEventCreateMouseEvent( - None, - release, - self.position, - mouse_button) - - # If we are performing a click, we need to set this state flag - if self._click is not None: - Quartz.CGEventSetIntegerValueField( - event, - Quartz.kCGMouseEventClickState, - self._click) - - Quartz.CGEventPost(Quartz.kCGHIDEventTap, event) - - if button == self._drag_button: - self._drag_button = None - - def __enter__(self): - self._click = 0 - return self - - def __exit__(self, exc_type, value, traceback): - self._click = None - - -class Listener(ListenerMixin, _base.Listener): - #: The events that we listen to - _EVENTS = ( - Quartz.CGEventMaskBit(Quartz.kCGEventMouseMoved) | - Quartz.CGEventMaskBit(Quartz.kCGEventLeftMouseDown) | - Quartz.CGEventMaskBit(Quartz.kCGEventLeftMouseUp) | - Quartz.CGEventMaskBit(Quartz.kCGEventLeftMouseDragged) | - Quartz.CGEventMaskBit(Quartz.kCGEventRightMouseDown) | - Quartz.CGEventMaskBit(Quartz.kCGEventRightMouseUp) | - Quartz.CGEventMaskBit(Quartz.kCGEventRightMouseDragged) | - Quartz.CGEventMaskBit(Quartz.kCGEventOtherMouseDown) | - Quartz.CGEventMaskBit(Quartz.kCGEventOtherMouseUp) | - Quartz.CGEventMaskBit(Quartz.kCGEventOtherMouseDragged) | - Quartz.CGEventMaskBit(Quartz.kCGEventScrollWheel)) - - def __init__(self, *args, **kwargs): - super(Listener, self).__init__(*args, **kwargs) - self._intercept = self._options.get( - 'intercept', - None) - - def _handle_message(self, _proxy, event_type, event, _refcon, injected): - """The callback registered with *macOS* for mouse events. - - This method will call the callbacks registered on initialisation. - """ - try: - (px, py) = Quartz.CGEventGetLocation(event) - except AttributeError: - # This happens during teardown of the virtual machine - return - - # Quickly detect the most common event type - if event_type == Quartz.kCGEventMouseMoved: - self.on_move(px, py, injected) - - elif event_type == Quartz.kCGEventScrollWheel: - dx = Quartz.CGEventGetIntegerValueField( - event, - Quartz.kCGScrollWheelEventDeltaAxis2) - dy = Quartz.CGEventGetIntegerValueField( - event, - Quartz.kCGScrollWheelEventDeltaAxis1) - self.on_scroll(px, py, dx, dy, injected) - - else: - for button in Button: - try: - (press, release, drag), _ = button.value - except TypeError: - # Button.unknown cannot be enumerated - continue - - # Press and release generate click events, and drag - # generates move events - if event_type in (press, release): - self.on_click(px, py, button, event_type == press, injected) - elif event_type == drag: - self.on_move(px, py, injected) diff --git a/PortablePython/Lib/site-packages/pynput/mouse/_dummy.py b/PortablePython/Lib/site-packages/pynput/mouse/_dummy.py deleted file mode 100644 index c0a8a74..0000000 --- a/PortablePython/Lib/site-packages/pynput/mouse/_dummy.py +++ /dev/null @@ -1,22 +0,0 @@ -# pynput -# Copyright (C) 2015-2024 Moses Palmér -# -# This program 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 3 of the License, or (at your option) any -# later version. -# -# This program 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 program. If not, see . -""" -This module contains a dummy implementation. - -It cannot be used, but importing it will not raise any exceptions. -""" - -from ._base import Button, Controller, Listener diff --git a/PortablePython/Lib/site-packages/pynput/mouse/_win32.py b/PortablePython/Lib/site-packages/pynput/mouse/_win32.py deleted file mode 100644 index 1c0dbb5..0000000 --- a/PortablePython/Lib/site-packages/pynput/mouse/_win32.py +++ /dev/null @@ -1,226 +0,0 @@ -# coding=utf-8 -# pynput -# Copyright (C) 2015-2024 Moses Palmér -# -# This program 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 3 of the License, or (at your option) any -# later version. -# -# This program 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 program. If not, see . -""" -The mouse implementation for *Windows*. -""" - -# pylint: disable=C0111 -# The documentation is extracted from the base classes - -# pylint: disable=R0903 -# We implement stubs - -import ctypes -import enum - -from ctypes import ( - windll, - wintypes) - -from pynput._util import NotifierMixin -from pynput._util.win32 import ( - INPUT, - INPUT_union, - ListenerMixin, - MOUSEINPUT, - SendInput, - SystemHook) -from . import _base - -#: A constant used as a factor when constructing mouse scroll data. -WHEEL_DELTA = 120 - - -class Button(enum.Enum): - """The various buttons. - """ - unknown = None - left = (MOUSEINPUT.LEFTUP, MOUSEINPUT.LEFTDOWN, 0) - middle = (MOUSEINPUT.MIDDLEUP, MOUSEINPUT.MIDDLEDOWN, 0) - right = (MOUSEINPUT.RIGHTUP, MOUSEINPUT.RIGHTDOWN, 0) - x1 = (MOUSEINPUT.XUP, MOUSEINPUT.XDOWN, MOUSEINPUT.XBUTTON1) - x2 = (MOUSEINPUT.XUP, MOUSEINPUT.XDOWN, MOUSEINPUT.XBUTTON2) - - -class Controller(NotifierMixin, _base.Controller): - __GetCursorPos = windll.user32.GetCursorPos - __SetCursorPos = windll.user32.SetCursorPos - - def __init__(self, *args, **kwargs): - super(Controller, self).__init__(*args, **kwargs) - - def _position_get(self): - point = wintypes.POINT() - if self.__GetCursorPos(ctypes.byref(point)): - return (point.x, point.y) - else: - return None - - def _position_set(self, pos): - pos = int(pos[0]), int(pos[1]) - self.__SetCursorPos(*pos) - self._emit('on_move', *pos, True) - - def _scroll(self, dx, dy): - if dy: - SendInput( - 1, - ctypes.byref(INPUT( - type=INPUT.MOUSE, - value=INPUT_union( - mi=MOUSEINPUT( - dwFlags=MOUSEINPUT.WHEEL, - mouseData=int(dy * WHEEL_DELTA))))), - ctypes.sizeof(INPUT)) - - if dx: - SendInput( - 1, - ctypes.byref(INPUT( - type=INPUT.MOUSE, - value=INPUT_union( - mi=MOUSEINPUT( - dwFlags=MOUSEINPUT.HWHEEL, - mouseData=int(dx * WHEEL_DELTA))))), - ctypes.sizeof(INPUT)) - - if dx or dy: - px, py = self._position_get() - self._emit('on_scroll', px, py, dx, dy, True) - - def _press(self, button): - SendInput( - 1, - ctypes.byref(INPUT( - type=INPUT.MOUSE, - value=INPUT_union( - mi=MOUSEINPUT( - dwFlags=button.value[1], - mouseData=button.value[2])))), - ctypes.sizeof(INPUT)) - - def _release(self, button): - SendInput( - 1, - ctypes.byref(INPUT( - type=INPUT.MOUSE, - value=INPUT_union( - mi=MOUSEINPUT( - dwFlags=button.value[0], - mouseData=button.value[2])))), - ctypes.sizeof(INPUT)) - - -@Controller._receiver -class Listener(ListenerMixin, _base.Listener): - #: The Windows hook ID for low level mouse events, ``WH_MOUSE_LL`` - _EVENTS = 14 - - WM_LBUTTONDOWN = 0x0201 - WM_LBUTTONUP = 0x0202 - WM_MBUTTONDOWN = 0x0207 - WM_MBUTTONUP = 0x0208 - WM_MOUSEMOVE = 0x0200 - WM_MOUSEWHEEL = 0x020A - WM_MOUSEHWHEEL = 0x020E - WM_RBUTTONDOWN = 0x0204 - WM_RBUTTONUP = 0x0205 - WM_XBUTTONDOWN = 0x20B - WM_XBUTTONUP = 0x20C - - MK_XBUTTON1 = 0x0020 - MK_XBUTTON2 = 0x0040 - - XBUTTON1 = 1 - XBUTTON2 = 2 - - #: A mapping from messages to button events - CLICK_BUTTONS = { - WM_LBUTTONDOWN: (Button.left, True), - WM_LBUTTONUP: (Button.left, False), - WM_MBUTTONDOWN: (Button.middle, True), - WM_MBUTTONUP: (Button.middle, False), - WM_RBUTTONDOWN: (Button.right, True), - WM_RBUTTONUP: (Button.right, False)} - - #: A mapping from message to X button events. - X_BUTTONS = { - WM_XBUTTONDOWN: { - XBUTTON1: (Button.x1, True), - XBUTTON2: (Button.x2, True)}, - WM_XBUTTONUP: { - XBUTTON1: (Button.x1, False), - XBUTTON2: (Button.x2, False)}} - - #: A mapping from messages to scroll vectors - SCROLL_BUTTONS = { - WM_MOUSEWHEEL: (0, 1), - WM_MOUSEHWHEEL: (1, 0)} - - _HANDLED_EXCEPTIONS = ( - SystemHook.SuppressException,) - - class _MSLLHOOKSTRUCT(ctypes.Structure): - """Contains information about a mouse event passed to a ``WH_MOUSE_LL`` - hook procedure, ``MouseProc``. - """ - LLMHF_INJECTED = 0x00000001 - LLMHF_LOWER_IL_INJECTED = 0x00000002 - _fields_ = [ - ('pt', wintypes.POINT), - ('mouseData', wintypes.DWORD), - ('flags', wintypes.DWORD), - ('time', wintypes.DWORD), - ('dwExtraInfo', ctypes.c_void_p)] - - #: A pointer to a :class:`_MSLLHOOKSTRUCT` - _LPMSLLHOOKSTRUCT = ctypes.POINTER(_MSLLHOOKSTRUCT) - - def __init__(self, *args, **kwargs): - super(Listener, self).__init__(*args, **kwargs) - self._event_filter = self._options.get( - 'event_filter', - lambda msg, data: True) - - def _handle_message(self, code, msg, lpdata): - if code != SystemHook.HC_ACTION: - return - - data = ctypes.cast(lpdata, self._LPMSLLHOOKSTRUCT).contents - injected = data.flags & (0 - | self._MSLLHOOKSTRUCT.LLMHF_INJECTED - | self._MSLLHOOKSTRUCT.LLMHF_LOWER_IL_INJECTED) != 0 - - # Suppress further propagation of the event if it is filtered - if self._event_filter(msg, data) is False: - return - - if msg == self.WM_MOUSEMOVE: - self.on_move(data.pt.x, data.pt.y,injected) - - elif msg in self.CLICK_BUTTONS: - button, pressed = self.CLICK_BUTTONS[msg] - self.on_click(data.pt.x, data.pt.y, button, pressed, injected) - - elif msg in self.X_BUTTONS: - button, pressed = self.X_BUTTONS[msg][data.mouseData >> 16] - self.on_click(data.pt.x, data.pt.y, button, pressed, injected) - - elif msg in self.SCROLL_BUTTONS: - mx, my = self.SCROLL_BUTTONS[msg] - dd = wintypes.SHORT(data.mouseData >> 16).value // WHEEL_DELTA - self.on_scroll(data.pt.x, data.pt.y, dd * mx, dd * my, injected) diff --git a/PortablePython/Lib/site-packages/pynput/mouse/_xorg.py b/PortablePython/Lib/site-packages/pynput/mouse/_xorg.py deleted file mode 100644 index 93b0a98..0000000 --- a/PortablePython/Lib/site-packages/pynput/mouse/_xorg.py +++ /dev/null @@ -1,184 +0,0 @@ -# coding=utf-8 -# pynput -# Copyright (C) 2015-2024 Moses Palmér -# -# This program 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 3 of the License, or (at your option) any -# later version. -# -# This program 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 program. If not, see . -""" -The keyboard implementation for *Xorg*. -""" - -# pylint: disable=C0111 -# The documentation is extracted from the base classes - - -# pylint: disable=E1101,E1102 -# We dynamically generate the Button class - -# pylint: disable=R0903 -# We implement stubs - -# pylint: disable=W0611 -try: - import pynput._util.xorg -except Exception as e: - raise ImportError('failed to acquire X connection: {}'.format(str(e)), e) -# pylint: enable=W0611 - -import enum -import Xlib.display -import Xlib.ext -import Xlib.ext.xtest -import Xlib.X -import Xlib.protocol - -from pynput._util.xorg import ( - display_manager, - ListenerMixin) -from . import _base - - -# pylint: disable=C0103 -Button = enum.Enum( - 'Button', - module=__name__, - names=[ - ('unknown', None), - ('left', 1), - ('middle', 2), - ('right', 3), - ('scroll_up', 4), - ('scroll_down', 5), - ('scroll_left', 6), - ('scroll_right', 7)] + [ - ('button%d' % i, i) - for i in range(8, 31)]) -# pylint: enable=C0103 - - -class Controller(_base.Controller): - def __init__(self, *args, **kwargs): - super(Controller, self).__init__(*args, **kwargs) - self._display = Xlib.display.Display() - - def __del__(self): - if hasattr(self, '_display'): - self._display.close() - - def _position_get(self): - with display_manager(self._display) as dm: - qp = dm.screen().root.query_pointer() - return (qp.root_x, qp.root_y) - - def _position_set(self, pos): - px, py = self._check_bounds(*pos) - with display_manager(self._display) as dm: - Xlib.ext.xtest.fake_input(dm, Xlib.X.MotionNotify, x=px, y=py) - - def _scroll(self, dx, dy): - dx, dy = self._check_bounds(dx, dy) - if dy: - self.click( - button=Button.scroll_up if dy > 0 else Button.scroll_down, - count=abs(dy)) - - if dx: - self.click( - button=Button.scroll_right if dx > 0 else Button.scroll_left, - count=abs(dx)) - - def _press(self, button): - with display_manager(self._display) as dm: - Xlib.ext.xtest.fake_input(dm, Xlib.X.ButtonPress, button.value) - - def _release(self, button): - with display_manager(self._display) as dm: - Xlib.ext.xtest.fake_input(dm, Xlib.X.ButtonRelease, button.value) - - def _check_bounds(self, *args): - """Checks the arguments and makes sure they are within the bounds of a - short integer. - - :param args: The values to verify. - """ - if not all( - (-0x7fff - 1) <= number <= 0x7fff - for number in args): - raise ValueError(args) - else: - return tuple(int(p) for p in args) - - -class Listener(ListenerMixin, _base.Listener): - #: A mapping from button values to scroll directions - _SCROLL_BUTTONS = { - Button.scroll_up.value: (0, 1), - Button.scroll_down.value: (0, -1), - Button.scroll_right.value: (1, 0), - Button.scroll_left.value: (-1, 0)} - - _EVENTS = ( - Xlib.X.ButtonPressMask, - Xlib.X.ButtonReleaseMask) - - def __init__(self, *args, **kwargs): - super(Listener, self).__init__(*args, **kwargs) - - def _handle_message(self, dummy_display, event, injected): - px = event.root_x - py = event.root_y - - if event.type == Xlib.X.ButtonPress: - # Scroll events are sent as button presses with the scroll - # button codes - scroll = self._SCROLL_BUTTONS.get(event.detail, None) - if scroll: - self.on_scroll( - px, py, scroll[0], scroll[1], injected) - else: - self.on_click( - px, py, self._button(event.detail), True, injected) - - elif event.type == Xlib.X.ButtonRelease: - # Send an event only if this was not a scroll event - if event.detail not in self._SCROLL_BUTTONS: - self.on_click( - px, py, self._button(event.detail), False, injected) - - else: - self.on_move(px, py, injected) - - - def _suppress_start(self, display): - display.screen().root.grab_pointer( - True, self._event_mask, Xlib.X.GrabModeAsync, Xlib.X.GrabModeAsync, - 0, 0, Xlib.X.CurrentTime) - - def _suppress_stop(self, display): - display.ungrab_pointer(Xlib.X.CurrentTime) - - # pylint: disable=R0201 - def _button(self, detail): - """Creates a mouse button from an event detail. - - If the button is unknown, :attr:`Button.unknown` is returned. - - :param detail: The event detail. - - :return: a button - """ - try: - return Button(detail) - except ValueError: - return Button.unknown - # pylint: enable=R0201 diff --git a/PortablePython/Lib/site-packages/six-1.17.0.dist-info/INSTALLER b/PortablePython/Lib/site-packages/six-1.17.0.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/PortablePython/Lib/site-packages/six-1.17.0.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/PortablePython/Lib/site-packages/six-1.17.0.dist-info/LICENSE b/PortablePython/Lib/site-packages/six-1.17.0.dist-info/LICENSE deleted file mode 100644 index 1cc22a5..0000000 --- a/PortablePython/Lib/site-packages/six-1.17.0.dist-info/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -Copyright (c) 2010-2024 Benjamin Peterson - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/PortablePython/Lib/site-packages/six-1.17.0.dist-info/METADATA b/PortablePython/Lib/site-packages/six-1.17.0.dist-info/METADATA deleted file mode 100644 index cfde03c..0000000 --- a/PortablePython/Lib/site-packages/six-1.17.0.dist-info/METADATA +++ /dev/null @@ -1,43 +0,0 @@ -Metadata-Version: 2.1 -Name: six -Version: 1.17.0 -Summary: Python 2 and 3 compatibility utilities -Home-page: https://github.com/benjaminp/six -Author: Benjamin Peterson -Author-email: benjamin@python.org -License: MIT -Classifier: Development Status :: 5 - Production/Stable -Classifier: Programming Language :: Python :: 2 -Classifier: Programming Language :: Python :: 3 -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: MIT License -Classifier: Topic :: Software Development :: Libraries -Classifier: Topic :: Utilities -Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.* -License-File: LICENSE - -.. image:: https://img.shields.io/pypi/v/six.svg - :target: https://pypi.org/project/six/ - :alt: six on PyPI - -.. image:: https://readthedocs.org/projects/six/badge/?version=latest - :target: https://six.readthedocs.io/ - :alt: six's documentation on Read the Docs - -.. image:: https://img.shields.io/badge/license-MIT-green.svg - :target: https://github.com/benjaminp/six/blob/master/LICENSE - :alt: MIT License badge - -Six is a Python 2 and 3 compatibility library. It provides utility functions -for smoothing over the differences between the Python versions with the goal of -writing Python code that is compatible on both Python versions. See the -documentation for more information on what is provided. - -Six supports Python 2.7 and 3.3+. It is contained in only one Python -file, so it can be easily copied into your project. (The copyright and license -notice must be retained.) - -Online documentation is at https://six.readthedocs.io/. - -Bugs can be reported to https://github.com/benjaminp/six. The code can also -be found there. diff --git a/PortablePython/Lib/site-packages/six-1.17.0.dist-info/RECORD b/PortablePython/Lib/site-packages/six-1.17.0.dist-info/RECORD deleted file mode 100644 index 603afd7..0000000 --- a/PortablePython/Lib/site-packages/six-1.17.0.dist-info/RECORD +++ /dev/null @@ -1,8 +0,0 @@ -__pycache__/six.cpython-313.pyc,, -six-1.17.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -six-1.17.0.dist-info/LICENSE,sha256=Q3W6IOK5xsTnytKUCmKP2Q6VzD1Q7pKq51VxXYuh-9A,1066 -six-1.17.0.dist-info/METADATA,sha256=ViBCB4wnUlSfbYp8htvF3XCAiKe-bYBnLsewcQC3JGg,1658 -six-1.17.0.dist-info/RECORD,, -six-1.17.0.dist-info/WHEEL,sha256=pxeNX5JdtCe58PUSYP9upmc7jdRPgvT0Gm9kb1SHlVw,109 -six-1.17.0.dist-info/top_level.txt,sha256=_iVH_iYEtEXnD8nYGQYpYFUvkUW9sEO1GYbkeKSAais,4 -six.py,sha256=xRyR9wPT1LNpbJI8tf7CE-BeddkhU5O--sfy-mo5BN8,34703 diff --git a/PortablePython/Lib/site-packages/six-1.17.0.dist-info/WHEEL b/PortablePython/Lib/site-packages/six-1.17.0.dist-info/WHEEL deleted file mode 100644 index 104f387..0000000 --- a/PortablePython/Lib/site-packages/six-1.17.0.dist-info/WHEEL +++ /dev/null @@ -1,6 +0,0 @@ -Wheel-Version: 1.0 -Generator: setuptools (75.6.0) -Root-Is-Purelib: true -Tag: py2-none-any -Tag: py3-none-any - diff --git a/PortablePython/Lib/site-packages/six-1.17.0.dist-info/top_level.txt b/PortablePython/Lib/site-packages/six-1.17.0.dist-info/top_level.txt deleted file mode 100644 index ffe2fce..0000000 --- a/PortablePython/Lib/site-packages/six-1.17.0.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -six diff --git a/PortablePython/Lib/site-packages/six.py b/PortablePython/Lib/site-packages/six.py deleted file mode 100644 index 3de5969..0000000 --- a/PortablePython/Lib/site-packages/six.py +++ /dev/null @@ -1,1003 +0,0 @@ -# Copyright (c) 2010-2024 Benjamin Peterson -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -"""Utilities for writing code that runs on Python 2 and 3""" - -from __future__ import absolute_import - -import functools -import itertools -import operator -import sys -import types - -__author__ = "Benjamin Peterson " -__version__ = "1.17.0" - - -# Useful for very coarse version differentiation. -PY2 = sys.version_info[0] == 2 -PY3 = sys.version_info[0] == 3 -PY34 = sys.version_info[0:2] >= (3, 4) - -if PY3: - string_types = str, - integer_types = int, - class_types = type, - text_type = str - binary_type = bytes - - MAXSIZE = sys.maxsize -else: - string_types = basestring, - integer_types = (int, long) - class_types = (type, types.ClassType) - text_type = unicode - binary_type = str - - if sys.platform.startswith("java"): - # Jython always uses 32 bits. - MAXSIZE = int((1 << 31) - 1) - else: - # It's possible to have sizeof(long) != sizeof(Py_ssize_t). - class X(object): - - def __len__(self): - return 1 << 31 - try: - len(X()) - except OverflowError: - # 32-bit - MAXSIZE = int((1 << 31) - 1) - else: - # 64-bit - MAXSIZE = int((1 << 63) - 1) - del X - -if PY34: - from importlib.util import spec_from_loader -else: - spec_from_loader = None - - -def _add_doc(func, doc): - """Add documentation to a function.""" - func.__doc__ = doc - - -def _import_module(name): - """Import module, returning the module after the last dot.""" - __import__(name) - return sys.modules[name] - - -class _LazyDescr(object): - - def __init__(self, name): - self.name = name - - def __get__(self, obj, tp): - result = self._resolve() - setattr(obj, self.name, result) # Invokes __set__. - try: - # This is a bit ugly, but it avoids running this again by - # removing this descriptor. - delattr(obj.__class__, self.name) - except AttributeError: - pass - return result - - -class MovedModule(_LazyDescr): - - def __init__(self, name, old, new=None): - super(MovedModule, self).__init__(name) - if PY3: - if new is None: - new = name - self.mod = new - else: - self.mod = old - - def _resolve(self): - return _import_module(self.mod) - - def __getattr__(self, attr): - _module = self._resolve() - value = getattr(_module, attr) - setattr(self, attr, value) - return value - - -class _LazyModule(types.ModuleType): - - def __init__(self, name): - super(_LazyModule, self).__init__(name) - self.__doc__ = self.__class__.__doc__ - - def __dir__(self): - attrs = ["__doc__", "__name__"] - attrs += [attr.name for attr in self._moved_attributes] - return attrs - - # Subclasses should override this - _moved_attributes = [] - - -class MovedAttribute(_LazyDescr): - - def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None): - super(MovedAttribute, self).__init__(name) - if PY3: - if new_mod is None: - new_mod = name - self.mod = new_mod - if new_attr is None: - if old_attr is None: - new_attr = name - else: - new_attr = old_attr - self.attr = new_attr - else: - self.mod = old_mod - if old_attr is None: - old_attr = name - self.attr = old_attr - - def _resolve(self): - module = _import_module(self.mod) - return getattr(module, self.attr) - - -class _SixMetaPathImporter(object): - - """ - A meta path importer to import six.moves and its submodules. - - This class implements a PEP302 finder and loader. It should be compatible - with Python 2.5 and all existing versions of Python3 - """ - - def __init__(self, six_module_name): - self.name = six_module_name - self.known_modules = {} - - def _add_module(self, mod, *fullnames): - for fullname in fullnames: - self.known_modules[self.name + "." + fullname] = mod - - def _get_module(self, fullname): - return self.known_modules[self.name + "." + fullname] - - def find_module(self, fullname, path=None): - if fullname in self.known_modules: - return self - return None - - def find_spec(self, fullname, path, target=None): - if fullname in self.known_modules: - return spec_from_loader(fullname, self) - return None - - def __get_module(self, fullname): - try: - return self.known_modules[fullname] - except KeyError: - raise ImportError("This loader does not know module " + fullname) - - def load_module(self, fullname): - try: - # in case of a reload - return sys.modules[fullname] - except KeyError: - pass - mod = self.__get_module(fullname) - if isinstance(mod, MovedModule): - mod = mod._resolve() - else: - mod.__loader__ = self - sys.modules[fullname] = mod - return mod - - def is_package(self, fullname): - """ - Return true, if the named module is a package. - - We need this method to get correct spec objects with - Python 3.4 (see PEP451) - """ - return hasattr(self.__get_module(fullname), "__path__") - - def get_code(self, fullname): - """Return None - - Required, if is_package is implemented""" - self.__get_module(fullname) # eventually raises ImportError - return None - get_source = get_code # same as get_code - - def create_module(self, spec): - return self.load_module(spec.name) - - def exec_module(self, module): - pass - -_importer = _SixMetaPathImporter(__name__) - - -class _MovedItems(_LazyModule): - - """Lazy loading of moved objects""" - __path__ = [] # mark as package - - -_moved_attributes = [ - MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"), - MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"), - MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"), - MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"), - MovedAttribute("intern", "__builtin__", "sys"), - MovedAttribute("map", "itertools", "builtins", "imap", "map"), - MovedAttribute("getcwd", "os", "os", "getcwdu", "getcwd"), - MovedAttribute("getcwdb", "os", "os", "getcwd", "getcwdb"), - MovedAttribute("getoutput", "commands", "subprocess"), - MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"), - MovedAttribute("reload_module", "__builtin__", "importlib" if PY34 else "imp", "reload"), - MovedAttribute("reduce", "__builtin__", "functools"), - MovedAttribute("shlex_quote", "pipes", "shlex", "quote"), - MovedAttribute("StringIO", "StringIO", "io"), - MovedAttribute("UserDict", "UserDict", "collections", "IterableUserDict", "UserDict"), - MovedAttribute("UserList", "UserList", "collections"), - MovedAttribute("UserString", "UserString", "collections"), - MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"), - MovedAttribute("zip", "itertools", "builtins", "izip", "zip"), - MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"), - MovedModule("builtins", "__builtin__"), - MovedModule("configparser", "ConfigParser"), - MovedModule("collections_abc", "collections", "collections.abc" if sys.version_info >= (3, 3) else "collections"), - MovedModule("copyreg", "copy_reg"), - MovedModule("dbm_gnu", "gdbm", "dbm.gnu"), - MovedModule("dbm_ndbm", "dbm", "dbm.ndbm"), - MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread" if sys.version_info < (3, 9) else "_thread"), - MovedModule("http_cookiejar", "cookielib", "http.cookiejar"), - MovedModule("http_cookies", "Cookie", "http.cookies"), - MovedModule("html_entities", "htmlentitydefs", "html.entities"), - MovedModule("html_parser", "HTMLParser", "html.parser"), - MovedModule("http_client", "httplib", "http.client"), - MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"), - MovedModule("email_mime_image", "email.MIMEImage", "email.mime.image"), - MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"), - MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"), - MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"), - MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"), - MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"), - MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"), - MovedModule("cPickle", "cPickle", "pickle"), - MovedModule("queue", "Queue"), - MovedModule("reprlib", "repr"), - MovedModule("socketserver", "SocketServer"), - MovedModule("_thread", "thread", "_thread"), - MovedModule("tkinter", "Tkinter"), - MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"), - MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"), - MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"), - MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"), - MovedModule("tkinter_tix", "Tix", "tkinter.tix"), - MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"), - MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"), - MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"), - MovedModule("tkinter_colorchooser", "tkColorChooser", - "tkinter.colorchooser"), - MovedModule("tkinter_commondialog", "tkCommonDialog", - "tkinter.commondialog"), - MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"), - MovedModule("tkinter_font", "tkFont", "tkinter.font"), - MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"), - MovedModule("tkinter_tksimpledialog", "tkSimpleDialog", - "tkinter.simpledialog"), - MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"), - MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"), - MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"), - MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"), - MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"), - MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"), -] -# Add windows specific modules. -if sys.platform == "win32": - _moved_attributes += [ - MovedModule("winreg", "_winreg"), - ] - -for attr in _moved_attributes: - setattr(_MovedItems, attr.name, attr) - if isinstance(attr, MovedModule): - _importer._add_module(attr, "moves." + attr.name) -del attr - -_MovedItems._moved_attributes = _moved_attributes - -moves = _MovedItems(__name__ + ".moves") -_importer._add_module(moves, "moves") - - -class Module_six_moves_urllib_parse(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_parse""" - - -_urllib_parse_moved_attributes = [ - MovedAttribute("ParseResult", "urlparse", "urllib.parse"), - MovedAttribute("SplitResult", "urlparse", "urllib.parse"), - MovedAttribute("parse_qs", "urlparse", "urllib.parse"), - MovedAttribute("parse_qsl", "urlparse", "urllib.parse"), - MovedAttribute("urldefrag", "urlparse", "urllib.parse"), - MovedAttribute("urljoin", "urlparse", "urllib.parse"), - MovedAttribute("urlparse", "urlparse", "urllib.parse"), - MovedAttribute("urlsplit", "urlparse", "urllib.parse"), - MovedAttribute("urlunparse", "urlparse", "urllib.parse"), - MovedAttribute("urlunsplit", "urlparse", "urllib.parse"), - MovedAttribute("quote", "urllib", "urllib.parse"), - MovedAttribute("quote_plus", "urllib", "urllib.parse"), - MovedAttribute("unquote", "urllib", "urllib.parse"), - MovedAttribute("unquote_plus", "urllib", "urllib.parse"), - MovedAttribute("unquote_to_bytes", "urllib", "urllib.parse", "unquote", "unquote_to_bytes"), - MovedAttribute("urlencode", "urllib", "urllib.parse"), - MovedAttribute("splitquery", "urllib", "urllib.parse"), - MovedAttribute("splittag", "urllib", "urllib.parse"), - MovedAttribute("splituser", "urllib", "urllib.parse"), - MovedAttribute("splitvalue", "urllib", "urllib.parse"), - MovedAttribute("uses_fragment", "urlparse", "urllib.parse"), - MovedAttribute("uses_netloc", "urlparse", "urllib.parse"), - MovedAttribute("uses_params", "urlparse", "urllib.parse"), - MovedAttribute("uses_query", "urlparse", "urllib.parse"), - MovedAttribute("uses_relative", "urlparse", "urllib.parse"), -] -for attr in _urllib_parse_moved_attributes: - setattr(Module_six_moves_urllib_parse, attr.name, attr) -del attr - -Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes - -_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"), - "moves.urllib_parse", "moves.urllib.parse") - - -class Module_six_moves_urllib_error(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_error""" - - -_urllib_error_moved_attributes = [ - MovedAttribute("URLError", "urllib2", "urllib.error"), - MovedAttribute("HTTPError", "urllib2", "urllib.error"), - MovedAttribute("ContentTooShortError", "urllib", "urllib.error"), -] -for attr in _urllib_error_moved_attributes: - setattr(Module_six_moves_urllib_error, attr.name, attr) -del attr - -Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes - -_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"), - "moves.urllib_error", "moves.urllib.error") - - -class Module_six_moves_urllib_request(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_request""" - - -_urllib_request_moved_attributes = [ - MovedAttribute("urlopen", "urllib2", "urllib.request"), - MovedAttribute("install_opener", "urllib2", "urllib.request"), - MovedAttribute("build_opener", "urllib2", "urllib.request"), - MovedAttribute("pathname2url", "urllib", "urllib.request"), - MovedAttribute("url2pathname", "urllib", "urllib.request"), - MovedAttribute("getproxies", "urllib", "urllib.request"), - MovedAttribute("Request", "urllib2", "urllib.request"), - MovedAttribute("OpenerDirector", "urllib2", "urllib.request"), - MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"), - MovedAttribute("ProxyHandler", "urllib2", "urllib.request"), - MovedAttribute("BaseHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"), - MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"), - MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"), - MovedAttribute("FileHandler", "urllib2", "urllib.request"), - MovedAttribute("FTPHandler", "urllib2", "urllib.request"), - MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"), - MovedAttribute("UnknownHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"), - MovedAttribute("urlretrieve", "urllib", "urllib.request"), - MovedAttribute("urlcleanup", "urllib", "urllib.request"), - MovedAttribute("proxy_bypass", "urllib", "urllib.request"), - MovedAttribute("parse_http_list", "urllib2", "urllib.request"), - MovedAttribute("parse_keqv_list", "urllib2", "urllib.request"), -] -if sys.version_info[:2] < (3, 14): - _urllib_request_moved_attributes.extend( - [ - MovedAttribute("URLopener", "urllib", "urllib.request"), - MovedAttribute("FancyURLopener", "urllib", "urllib.request"), - ] - ) -for attr in _urllib_request_moved_attributes: - setattr(Module_six_moves_urllib_request, attr.name, attr) -del attr - -Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes - -_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"), - "moves.urllib_request", "moves.urllib.request") - - -class Module_six_moves_urllib_response(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_response""" - - -_urllib_response_moved_attributes = [ - MovedAttribute("addbase", "urllib", "urllib.response"), - MovedAttribute("addclosehook", "urllib", "urllib.response"), - MovedAttribute("addinfo", "urllib", "urllib.response"), - MovedAttribute("addinfourl", "urllib", "urllib.response"), -] -for attr in _urllib_response_moved_attributes: - setattr(Module_six_moves_urllib_response, attr.name, attr) -del attr - -Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes - -_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"), - "moves.urllib_response", "moves.urllib.response") - - -class Module_six_moves_urllib_robotparser(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_robotparser""" - - -_urllib_robotparser_moved_attributes = [ - MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"), -] -for attr in _urllib_robotparser_moved_attributes: - setattr(Module_six_moves_urllib_robotparser, attr.name, attr) -del attr - -Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes - -_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"), - "moves.urllib_robotparser", "moves.urllib.robotparser") - - -class Module_six_moves_urllib(types.ModuleType): - - """Create a six.moves.urllib namespace that resembles the Python 3 namespace""" - __path__ = [] # mark as package - parse = _importer._get_module("moves.urllib_parse") - error = _importer._get_module("moves.urllib_error") - request = _importer._get_module("moves.urllib_request") - response = _importer._get_module("moves.urllib_response") - robotparser = _importer._get_module("moves.urllib_robotparser") - - def __dir__(self): - return ['parse', 'error', 'request', 'response', 'robotparser'] - -_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"), - "moves.urllib") - - -def add_move(move): - """Add an item to six.moves.""" - setattr(_MovedItems, move.name, move) - - -def remove_move(name): - """Remove item from six.moves.""" - try: - delattr(_MovedItems, name) - except AttributeError: - try: - del moves.__dict__[name] - except KeyError: - raise AttributeError("no such move, %r" % (name,)) - - -if PY3: - _meth_func = "__func__" - _meth_self = "__self__" - - _func_closure = "__closure__" - _func_code = "__code__" - _func_defaults = "__defaults__" - _func_globals = "__globals__" -else: - _meth_func = "im_func" - _meth_self = "im_self" - - _func_closure = "func_closure" - _func_code = "func_code" - _func_defaults = "func_defaults" - _func_globals = "func_globals" - - -try: - advance_iterator = next -except NameError: - def advance_iterator(it): - return it.next() -next = advance_iterator - - -try: - callable = callable -except NameError: - def callable(obj): - return any("__call__" in klass.__dict__ for klass in type(obj).__mro__) - - -if PY3: - def get_unbound_function(unbound): - return unbound - - create_bound_method = types.MethodType - - def create_unbound_method(func, cls): - return func - - Iterator = object -else: - def get_unbound_function(unbound): - return unbound.im_func - - def create_bound_method(func, obj): - return types.MethodType(func, obj, obj.__class__) - - def create_unbound_method(func, cls): - return types.MethodType(func, None, cls) - - class Iterator(object): - - def next(self): - return type(self).__next__(self) - - callable = callable -_add_doc(get_unbound_function, - """Get the function out of a possibly unbound function""") - - -get_method_function = operator.attrgetter(_meth_func) -get_method_self = operator.attrgetter(_meth_self) -get_function_closure = operator.attrgetter(_func_closure) -get_function_code = operator.attrgetter(_func_code) -get_function_defaults = operator.attrgetter(_func_defaults) -get_function_globals = operator.attrgetter(_func_globals) - - -if PY3: - def iterkeys(d, **kw): - return iter(d.keys(**kw)) - - def itervalues(d, **kw): - return iter(d.values(**kw)) - - def iteritems(d, **kw): - return iter(d.items(**kw)) - - def iterlists(d, **kw): - return iter(d.lists(**kw)) - - viewkeys = operator.methodcaller("keys") - - viewvalues = operator.methodcaller("values") - - viewitems = operator.methodcaller("items") -else: - def iterkeys(d, **kw): - return d.iterkeys(**kw) - - def itervalues(d, **kw): - return d.itervalues(**kw) - - def iteritems(d, **kw): - return d.iteritems(**kw) - - def iterlists(d, **kw): - return d.iterlists(**kw) - - viewkeys = operator.methodcaller("viewkeys") - - viewvalues = operator.methodcaller("viewvalues") - - viewitems = operator.methodcaller("viewitems") - -_add_doc(iterkeys, "Return an iterator over the keys of a dictionary.") -_add_doc(itervalues, "Return an iterator over the values of a dictionary.") -_add_doc(iteritems, - "Return an iterator over the (key, value) pairs of a dictionary.") -_add_doc(iterlists, - "Return an iterator over the (key, [values]) pairs of a dictionary.") - - -if PY3: - def b(s): - return s.encode("latin-1") - - def u(s): - return s - unichr = chr - import struct - int2byte = struct.Struct(">B").pack - del struct - byte2int = operator.itemgetter(0) - indexbytes = operator.getitem - iterbytes = iter - import io - StringIO = io.StringIO - BytesIO = io.BytesIO - del io - _assertCountEqual = "assertCountEqual" - if sys.version_info[1] <= 1: - _assertRaisesRegex = "assertRaisesRegexp" - _assertRegex = "assertRegexpMatches" - _assertNotRegex = "assertNotRegexpMatches" - else: - _assertRaisesRegex = "assertRaisesRegex" - _assertRegex = "assertRegex" - _assertNotRegex = "assertNotRegex" -else: - def b(s): - return s - # Workaround for standalone backslash - - def u(s): - return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape") - unichr = unichr - int2byte = chr - - def byte2int(bs): - return ord(bs[0]) - - def indexbytes(buf, i): - return ord(buf[i]) - iterbytes = functools.partial(itertools.imap, ord) - import StringIO - StringIO = BytesIO = StringIO.StringIO - _assertCountEqual = "assertItemsEqual" - _assertRaisesRegex = "assertRaisesRegexp" - _assertRegex = "assertRegexpMatches" - _assertNotRegex = "assertNotRegexpMatches" -_add_doc(b, """Byte literal""") -_add_doc(u, """Text literal""") - - -def assertCountEqual(self, *args, **kwargs): - return getattr(self, _assertCountEqual)(*args, **kwargs) - - -def assertRaisesRegex(self, *args, **kwargs): - return getattr(self, _assertRaisesRegex)(*args, **kwargs) - - -def assertRegex(self, *args, **kwargs): - return getattr(self, _assertRegex)(*args, **kwargs) - - -def assertNotRegex(self, *args, **kwargs): - return getattr(self, _assertNotRegex)(*args, **kwargs) - - -if PY3: - exec_ = getattr(moves.builtins, "exec") - - def reraise(tp, value, tb=None): - try: - if value is None: - value = tp() - if value.__traceback__ is not tb: - raise value.with_traceback(tb) - raise value - finally: - value = None - tb = None - -else: - def exec_(_code_, _globs_=None, _locs_=None): - """Execute code in a namespace.""" - if _globs_ is None: - frame = sys._getframe(1) - _globs_ = frame.f_globals - if _locs_ is None: - _locs_ = frame.f_locals - del frame - elif _locs_ is None: - _locs_ = _globs_ - exec("""exec _code_ in _globs_, _locs_""") - - exec_("""def reraise(tp, value, tb=None): - try: - raise tp, value, tb - finally: - tb = None -""") - - -if sys.version_info[:2] > (3,): - exec_("""def raise_from(value, from_value): - try: - raise value from from_value - finally: - value = None -""") -else: - def raise_from(value, from_value): - raise value - - -print_ = getattr(moves.builtins, "print", None) -if print_ is None: - def print_(*args, **kwargs): - """The new-style print function for Python 2.4 and 2.5.""" - fp = kwargs.pop("file", sys.stdout) - if fp is None: - return - - def write(data): - if not isinstance(data, basestring): - data = str(data) - # If the file has an encoding, encode unicode with it. - if (isinstance(fp, file) and - isinstance(data, unicode) and - fp.encoding is not None): - errors = getattr(fp, "errors", None) - if errors is None: - errors = "strict" - data = data.encode(fp.encoding, errors) - fp.write(data) - want_unicode = False - sep = kwargs.pop("sep", None) - if sep is not None: - if isinstance(sep, unicode): - want_unicode = True - elif not isinstance(sep, str): - raise TypeError("sep must be None or a string") - end = kwargs.pop("end", None) - if end is not None: - if isinstance(end, unicode): - want_unicode = True - elif not isinstance(end, str): - raise TypeError("end must be None or a string") - if kwargs: - raise TypeError("invalid keyword arguments to print()") - if not want_unicode: - for arg in args: - if isinstance(arg, unicode): - want_unicode = True - break - if want_unicode: - newline = unicode("\n") - space = unicode(" ") - else: - newline = "\n" - space = " " - if sep is None: - sep = space - if end is None: - end = newline - for i, arg in enumerate(args): - if i: - write(sep) - write(arg) - write(end) -if sys.version_info[:2] < (3, 3): - _print = print_ - - def print_(*args, **kwargs): - fp = kwargs.get("file", sys.stdout) - flush = kwargs.pop("flush", False) - _print(*args, **kwargs) - if flush and fp is not None: - fp.flush() - -_add_doc(reraise, """Reraise an exception.""") - -if sys.version_info[0:2] < (3, 4): - # This does exactly the same what the :func:`py3:functools.update_wrapper` - # function does on Python versions after 3.2. It sets the ``__wrapped__`` - # attribute on ``wrapper`` object and it doesn't raise an error if any of - # the attributes mentioned in ``assigned`` and ``updated`` are missing on - # ``wrapped`` object. - def _update_wrapper(wrapper, wrapped, - assigned=functools.WRAPPER_ASSIGNMENTS, - updated=functools.WRAPPER_UPDATES): - for attr in assigned: - try: - value = getattr(wrapped, attr) - except AttributeError: - continue - else: - setattr(wrapper, attr, value) - for attr in updated: - getattr(wrapper, attr).update(getattr(wrapped, attr, {})) - wrapper.__wrapped__ = wrapped - return wrapper - _update_wrapper.__doc__ = functools.update_wrapper.__doc__ - - def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS, - updated=functools.WRAPPER_UPDATES): - return functools.partial(_update_wrapper, wrapped=wrapped, - assigned=assigned, updated=updated) - wraps.__doc__ = functools.wraps.__doc__ - -else: - wraps = functools.wraps - - -def with_metaclass(meta, *bases): - """Create a base class with a metaclass.""" - # This requires a bit of explanation: the basic idea is to make a dummy - # metaclass for one level of class instantiation that replaces itself with - # the actual metaclass. - class metaclass(type): - - def __new__(cls, name, this_bases, d): - if sys.version_info[:2] >= (3, 7): - # This version introduced PEP 560 that requires a bit - # of extra care (we mimic what is done by __build_class__). - resolved_bases = types.resolve_bases(bases) - if resolved_bases is not bases: - d['__orig_bases__'] = bases - else: - resolved_bases = bases - return meta(name, resolved_bases, d) - - @classmethod - def __prepare__(cls, name, this_bases): - return meta.__prepare__(name, bases) - return type.__new__(metaclass, 'temporary_class', (), {}) - - -def add_metaclass(metaclass): - """Class decorator for creating a class with a metaclass.""" - def wrapper(cls): - orig_vars = cls.__dict__.copy() - slots = orig_vars.get('__slots__') - if slots is not None: - if isinstance(slots, str): - slots = [slots] - for slots_var in slots: - orig_vars.pop(slots_var) - orig_vars.pop('__dict__', None) - orig_vars.pop('__weakref__', None) - if hasattr(cls, '__qualname__'): - orig_vars['__qualname__'] = cls.__qualname__ - return metaclass(cls.__name__, cls.__bases__, orig_vars) - return wrapper - - -def ensure_binary(s, encoding='utf-8', errors='strict'): - """Coerce **s** to six.binary_type. - - For Python 2: - - `unicode` -> encoded to `str` - - `str` -> `str` - - For Python 3: - - `str` -> encoded to `bytes` - - `bytes` -> `bytes` - """ - if isinstance(s, binary_type): - return s - if isinstance(s, text_type): - return s.encode(encoding, errors) - raise TypeError("not expecting type '%s'" % type(s)) - - -def ensure_str(s, encoding='utf-8', errors='strict'): - """Coerce *s* to `str`. - - For Python 2: - - `unicode` -> encoded to `str` - - `str` -> `str` - - For Python 3: - - `str` -> `str` - - `bytes` -> decoded to `str` - """ - # Optimization: Fast return for the common case. - if type(s) is str: - return s - if PY2 and isinstance(s, text_type): - return s.encode(encoding, errors) - elif PY3 and isinstance(s, binary_type): - return s.decode(encoding, errors) - elif not isinstance(s, (text_type, binary_type)): - raise TypeError("not expecting type '%s'" % type(s)) - return s - - -def ensure_text(s, encoding='utf-8', errors='strict'): - """Coerce *s* to six.text_type. - - For Python 2: - - `unicode` -> `unicode` - - `str` -> `unicode` - - For Python 3: - - `str` -> `str` - - `bytes` -> decoded to `str` - """ - if isinstance(s, binary_type): - return s.decode(encoding, errors) - elif isinstance(s, text_type): - return s - else: - raise TypeError("not expecting type '%s'" % type(s)) - - -def python_2_unicode_compatible(klass): - """ - A class decorator that defines __unicode__ and __str__ methods under Python 2. - Under Python 3 it does nothing. - - To support Python 2 and 3 with a single code base, define a __str__ method - returning text and apply this decorator to the class. - """ - if PY2: - if '__str__' not in klass.__dict__: - raise ValueError("@python_2_unicode_compatible cannot be applied " - "to %s because it doesn't define __str__()." % - klass.__name__) - klass.__unicode__ = klass.__str__ - klass.__str__ = lambda self: self.__unicode__().encode('utf-8') - return klass - - -# Complete the moves implementation. -# This code is at the end of this module to speed up module loading. -# Turn this module into a package. -__path__ = [] # required for PEP 302 and PEP 451 -__package__ = __name__ # see PEP 366 @ReservedAssignment -if globals().get("__spec__") is not None: - __spec__.submodule_search_locations = [] # PEP 451 @UndefinedVariable -# Remove other six meta path importers, since they cause problems. This can -# happen if six is removed from sys.modules and then reloaded. (Setuptools does -# this for some reason.) -if sys.meta_path: - for i, importer in enumerate(sys.meta_path): - # Here's some real nastiness: Another "instance" of the six module might - # be floating around. Therefore, we can't use isinstance() to check for - # the six meta path importer, since the other six instance will have - # inserted an importer with different class. - if (type(importer).__name__ == "_SixMetaPathImporter" and - importer.name == __name__): - del sys.meta_path[i] - break - del i, importer -# Finally, add the importer to the meta path import hook. -sys.meta_path.append(_importer) diff --git a/PortablePython/_asyncio.pyd b/PortablePython/_asyncio.pyd deleted file mode 100644 index dd16af6..0000000 Binary files a/PortablePython/_asyncio.pyd and /dev/null differ diff --git a/PortablePython/_bz2.pyd b/PortablePython/_bz2.pyd deleted file mode 100644 index efcfffe..0000000 Binary files a/PortablePython/_bz2.pyd and /dev/null differ diff --git a/PortablePython/_ctypes.pyd b/PortablePython/_ctypes.pyd deleted file mode 100644 index 018eda1..0000000 Binary files a/PortablePython/_ctypes.pyd and /dev/null differ diff --git a/PortablePython/_decimal.pyd b/PortablePython/_decimal.pyd deleted file mode 100644 index 7dfb600..0000000 Binary files a/PortablePython/_decimal.pyd and /dev/null differ diff --git a/PortablePython/_elementtree.pyd b/PortablePython/_elementtree.pyd deleted file mode 100644 index c0436ac..0000000 Binary files a/PortablePython/_elementtree.pyd and /dev/null differ diff --git a/PortablePython/_hashlib.pyd b/PortablePython/_hashlib.pyd deleted file mode 100644 index c240327..0000000 Binary files a/PortablePython/_hashlib.pyd and /dev/null differ diff --git a/PortablePython/_lzma.pyd b/PortablePython/_lzma.pyd deleted file mode 100644 index 98781ed..0000000 Binary files a/PortablePython/_lzma.pyd and /dev/null differ diff --git a/PortablePython/_multiprocessing.pyd b/PortablePython/_multiprocessing.pyd deleted file mode 100644 index 1cb0d95..0000000 Binary files a/PortablePython/_multiprocessing.pyd and /dev/null differ diff --git a/PortablePython/_overlapped.pyd b/PortablePython/_overlapped.pyd deleted file mode 100644 index 5ea0657..0000000 Binary files a/PortablePython/_overlapped.pyd and /dev/null differ diff --git a/PortablePython/_queue.pyd b/PortablePython/_queue.pyd deleted file mode 100644 index 7632b73..0000000 Binary files a/PortablePython/_queue.pyd and /dev/null differ diff --git a/PortablePython/_remote_debugging.pyd b/PortablePython/_remote_debugging.pyd deleted file mode 100644 index f29eddd..0000000 Binary files a/PortablePython/_remote_debugging.pyd and /dev/null differ diff --git a/PortablePython/_socket.pyd b/PortablePython/_socket.pyd deleted file mode 100644 index e249f30..0000000 Binary files a/PortablePython/_socket.pyd and /dev/null differ diff --git a/PortablePython/_sqlite3.pyd b/PortablePython/_sqlite3.pyd deleted file mode 100644 index 52e4bc2..0000000 Binary files a/PortablePython/_sqlite3.pyd and /dev/null differ diff --git a/PortablePython/_ssl.pyd b/PortablePython/_ssl.pyd deleted file mode 100644 index 2bacca6..0000000 Binary files a/PortablePython/_ssl.pyd and /dev/null differ diff --git a/PortablePython/_uuid.pyd b/PortablePython/_uuid.pyd deleted file mode 100644 index 9e447e0..0000000 Binary files a/PortablePython/_uuid.pyd and /dev/null differ diff --git a/PortablePython/_wmi.pyd b/PortablePython/_wmi.pyd deleted file mode 100644 index 2ac520f..0000000 Binary files a/PortablePython/_wmi.pyd and /dev/null differ diff --git a/PortablePython/_zoneinfo.pyd b/PortablePython/_zoneinfo.pyd deleted file mode 100644 index db27d13..0000000 Binary files a/PortablePython/_zoneinfo.pyd and /dev/null differ diff --git a/PortablePython/_zstd.pyd b/PortablePython/_zstd.pyd deleted file mode 100644 index 2a3fe89..0000000 Binary files a/PortablePython/_zstd.pyd and /dev/null differ diff --git a/PortablePython/libcrypto-3.dll b/PortablePython/libcrypto-3.dll deleted file mode 100644 index b7a525a..0000000 Binary files a/PortablePython/libcrypto-3.dll and /dev/null differ diff --git a/PortablePython/libffi-8.dll b/PortablePython/libffi-8.dll deleted file mode 100644 index 8ebbbe8..0000000 Binary files a/PortablePython/libffi-8.dll and /dev/null differ diff --git a/PortablePython/libssl-3.dll b/PortablePython/libssl-3.dll deleted file mode 100644 index 5a53a34..0000000 Binary files a/PortablePython/libssl-3.dll and /dev/null differ diff --git a/PortablePython/pyexpat.pyd b/PortablePython/pyexpat.pyd deleted file mode 100644 index f884b92..0000000 Binary files a/PortablePython/pyexpat.pyd and /dev/null differ diff --git a/PortablePython/python.cat b/PortablePython/python.cat deleted file mode 100644 index b8125f9..0000000 Binary files a/PortablePython/python.cat and /dev/null differ diff --git a/PortablePython/python.exe b/PortablePython/python.exe deleted file mode 100644 index d478dae..0000000 Binary files a/PortablePython/python.exe and /dev/null differ diff --git a/PortablePython/python3.dll b/PortablePython/python3.dll deleted file mode 100644 index 0868b22..0000000 Binary files a/PortablePython/python3.dll and /dev/null differ diff --git a/PortablePython/python314._pth b/PortablePython/python314._pth deleted file mode 100644 index b764f7c..0000000 --- a/PortablePython/python314._pth +++ /dev/null @@ -1,5 +0,0 @@ -python314.zip -. - -# Uncomment to run site.main() automatically -#import site diff --git a/PortablePython/python314.dll b/PortablePython/python314.dll deleted file mode 100644 index 1a69300..0000000 Binary files a/PortablePython/python314.dll and /dev/null differ diff --git a/PortablePython/python314.zip b/PortablePython/python314.zip deleted file mode 100644 index ec77c58..0000000 Binary files a/PortablePython/python314.zip and /dev/null differ diff --git a/PortablePython/pythonw.exe b/PortablePython/pythonw.exe deleted file mode 100644 index e70b3da..0000000 Binary files a/PortablePython/pythonw.exe and /dev/null differ diff --git a/PortablePython/select.pyd b/PortablePython/select.pyd deleted file mode 100644 index 723a38b..0000000 Binary files a/PortablePython/select.pyd and /dev/null differ diff --git a/PortablePython/sqlite3.dll b/PortablePython/sqlite3.dll deleted file mode 100644 index 8227b2e..0000000 Binary files a/PortablePython/sqlite3.dll and /dev/null differ diff --git a/PortablePython/unicodedata.pyd b/PortablePython/unicodedata.pyd deleted file mode 100644 index ba9a464..0000000 Binary files a/PortablePython/unicodedata.pyd and /dev/null differ diff --git a/PortablePython/vcruntime140.dll b/PortablePython/vcruntime140.dll deleted file mode 100644 index 411009b..0000000 Binary files a/PortablePython/vcruntime140.dll and /dev/null differ diff --git a/PortablePython/vcruntime140_1.dll b/PortablePython/vcruntime140_1.dll deleted file mode 100644 index 2071f28..0000000 Binary files a/PortablePython/vcruntime140_1.dll and /dev/null differ diff --git a/PortablePython/winsound.pyd b/PortablePython/winsound.pyd deleted file mode 100644 index 7cc5224..0000000 Binary files a/PortablePython/winsound.pyd and /dev/null differ diff --git a/README.md b/README.md index 4892197..093224d 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,8 @@ 监听鼠标右键事件,在右键按下时模拟键盘按下「屏息」键、右键松开时释放。脚本通过进程名识别游戏,只有游戏在运行时才激活监听,游戏关闭自动停止,重新进入会自动恢复。 +> **仅支持「按住开镜」模式**:脚本依赖鼠标右键的按下/松开事件触发屏息键的按下/释放,游戏内的开镜方式必须设为「按住」而不是「切换/按键开镜」。否则松开右键时屏息会被释放,但游戏仍处于开镜状态,行为不一致。 + 纯本地按键映射,等价于支持宏的游戏鼠标硬件功能;不读写游戏内存、不修改任何游戏文件、不与游戏服务器通信。 ## 使用方法 @@ -14,9 +16,17 @@ 打开三角洲行动设置,把「屏息」绑定到一个不会和其他操作冲突的按键,例如 `F12`。记住这个按键。 -### 2. 修改配置文件 +### 2. 下载并解压 -打开 `config.ini`,按需调整: +到本仓库 [Releases](../../releases) 页面下载最新的 `df-scope-hold-vX.X.X.zip`,解压到任意目录,里面包含: + +- `df-scope-hold.exe`:主程序(已内嵌管理员权限 manifest,双击即弹 UAC) +- `config.ini`:默认配置 +- `README.md`:本说明 + +### 3. 修改配置文件 + +打开同目录下的 `config.ini`,按需调整: ```ini [config] @@ -25,11 +35,11 @@ delay_press = 5 ; 右键按下到模拟 program_running = DeltaForceClient-Win64-Shipping.exe ; 游戏进程名(区分大小写) ``` -### 3. 运行 +### 4. 运行 -**右键 `run.bat` → 以管理员身份运行**。游戏客户端带反作弊保护通常以管理员权限运行,普通权限的脚本无法检测到游戏进程,必须以同等权限运行才能正常工作。 +双击 `df-scope-hold.exe`,Windows 会弹出 UAC 提示申请管理员权限,**必须允许**。游戏客户端带反作弊保护通常以管理员权限运行,普通权限的进程无法检测到游戏进程。 -脚本启动后会在命令行窗口提示当前配置并等待游戏启动,游戏运行起来后开始监听,按 `Ctrl+C` 退出。 +启动后会在命令行窗口提示当前配置并等待游戏启动,游戏运行起来后开始监听,按 `Ctrl+C` 退出。 ## 配置说明 @@ -41,10 +51,28 @@ program_running = DeltaForceClient-Win64-Shipping.exe ; 游戏进程名(区 ## 运行环境 -仓库已内置 `PortablePython/`(约 25MB),包含运行所需的 Python 3 解释器与依赖(`pynput`、`psutil`),**无需另外安装 Python**,下载即用。 +Windows 单 exe 分发(约 10MB),由 PyInstaller 把 Python 解释器与依赖(`pynput`、`psutil`)一起打包进可执行文件,**用户无需安装任何 Python 环境**。 仅在 Windows 上验证过(三角洲行动客户端为 Windows 独占)。 +## 从源码运行(开发者) + +```bash +pip install -r requirements.txt +python script.py +``` + +需要 Python 3.10+。日常使用直接下载 release 即可,无需自行构建。 + +## 自行构建 + +```bash +pip install -r requirements.txt +pyinstaller --onefile --uac-admin --console --name df-scope-hold script.py +``` + +产物在 `dist/df-scope-hold.exe`。仓库已配置 GitHub / Gitea Actions,推 `v*` tag 后自动构建并发布到 Releases,无需本地操作。 + ## 免责声明 - 本脚本是纯本地的鼠标→键盘映射工具,原理与游戏鼠标厂商提供的宏功能完全一致 diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..14aa244 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +pynput==1.8.1 +psutil==7.0.0 +pyinstaller==6.11.1 diff --git a/run.bat b/run.bat deleted file mode 100644 index 404604b..0000000 --- a/run.bat +++ /dev/null @@ -1,7 +0,0 @@ -@echo off -REM 设置便携式Python路径和脚本路径 -set PYTHON_PATH="%~dp0PortablePython\python.exe" -set SCRIPT_PATH="%~dp0script.py" - -REM 动态传递所有参数给Python脚本 -%PYTHON_PATH% %SCRIPT_PATH% \ No newline at end of file diff --git a/script.py b/script.py index 8fcf540..a512062 100644 --- a/script.py +++ b/script.py @@ -1,7 +1,11 @@ import os import sys -current_dir = os.path.dirname(os.path.realpath(__file__)) -sys.path.append(os.path.join(current_dir, "PortablePython", "Lib", "site-packages")) + +if getattr(sys, 'frozen', False): + # PyInstaller onefile:配置文件应在 exe 同目录,而非 _MEI 临时解压目录 + current_dir = os.path.dirname(sys.executable) +else: + current_dir = os.path.dirname(os.path.realpath(__file__)) import time from pynput import mouse, keyboard