From e95762885e7ca6eab73b92706f7f5c19bcb55d0d Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Fri, 20 Dec 2024 11:05:28 +0100 Subject: [PATCH] Drop Python 2.7 support (#2481) About dropping Python 2.7 support, 3 years ago [I stated](https://github.com/giampaolo/psutil/issues/2014#issuecomment-969263432): > [...] not a chance, for many years to come. [Python 2.7] currently represents 7-10% of total downloads, aka around 70k /100k downloads per day 3 years later (and to my surprise) **downloads for Python 2.7 dropped to 0.36%**. These are downloads per month: ``` $ pypinfo --percent psutil pyversion Served from cache: False Data processed: 4.65 GiB Data billed: 4.65 GiB Estimated cost: $0.03 | python_version | percent | download_count | | -------------- | ------- | -------------- | | 3.10 | 23.84% | 26,354,506 | | 3.8 | 18.87% | 20,862,015 | | 3.7 | 17.38% | 19,217,960 | | 3.9 | 17.00% | 18,798,843 | | 3.11 | 13.63% | 15,066,706 | | 3.12 | 7.01% | 7,754,751 | | 3.13 | 1.15% | 1,267,008 | | 3.6 | 0.73% | 803,189 | | 2.7 | 0.36% | 402,111 | | 3.5 | 0.03% | 28,656 | | Total | | 110,555,745 | ``` According to [pypistats.org](https://archive.is/wip/knzql) it's 0.28% of the total, and around 15.000 downloads per day. Maintaining 2.7 support has become increasingly difficult, but still possible. E.g. we can still run tests by using [old PYPI backports](https://github.com/giampaolo/psutil/blob/fbb6d9ce98f930d3d101b7df5a4f4d0f1d2b35a3/setup.py#L76-L85). GitHub Actions can still be [tweaked](https://github.com/giampaolo/psutil/blob/fbb6d9ce98f930d3d101b7df5a4f4d0f1d2b35a3/.github/workflows/build.yml#L77-L112) to run tests and produce wheels on Linux and macOS. Not Windows though, for which we have to use a separate service (Appveyor). Still, the amount of hacks in psutil source code necessary to support Python 2.7 piled up over the years, and became quite big. Some disadvantages that come to mind: * (high) having to maintain various python compatibility layers (e.g. [psutil/_compat.py](https://github.com/giampaolo/psutil/blob/fbb6d9ce98f930d3d101b7df5a4f4d0f1d2b35a3/psutil/_compat.py#L1)) + all the compromises that come with it (extra imports, extra code, str vs. unicode differences, etc.) * (medium) having to maintain a C compatibility layer (`#if PY_MAJOR_VERSION <= 3`, etc.) * (medium) inability to use modern language features, especially f-strings * (low) inability to freely use `enum`s, which creates a difference on how CONSTANTS are exposed in terms of API * (medium) having to install a specific version of pip and other (outdated) [deps](https://github.com/giampaolo/psutil/blob/fbb6d9ce98f930d3d101b7df5a4f4d0f1d2b35a3/setup.py#L76-L85) * (high) relying on third-party Appveyor CI service, just to run tests on python 2.7 and produce wheels, when we could rely on a single CI service instead (GitHub) * (high) soon I want to distribute wheels via GitHub instead of manually via `twine`, so that'll be a problem (CC @potiuk) * (high) gradual lack of support from third-party libraries and services * (medium) 4 extra CI jobs which are run on every commit (Linux, macOS, Windows 32-bit, Windows 64-bit) * (medium) the distribution of 7 wheels specific for Python 2.7. From last release: * psutil-6.1.1-cp27-cp27m-macosx_10_9_x86_64.whl * psutil-6.1.1-cp27-none-win32.whl * psutil-6.1.1-cp27-none-win_amd64.whl * psutil-6.1.1-cp27-cp27m-manylinux2010_i686.whl * psutil-6.1.1-cp27-cp27m-manylinux2010_x86_64.whl * psutil-6.1.1-cp27-cp27mu-manylinux2010_i686.whl * psutil-6.1.1-cp27-cp27mu-manylinux2010_x86_64.whl * etc. As such I decided to finally **drop support for Python 2.7**. Current psutil 6.1.1 release will still support Python 2.7, but next 7.0.0 will not. We can still make a promise that the 6.1.* line (EDIT: see [python2 branch](https://github.com/giampaolo/psutil/tree/python2)) will keep supporting Python 2.7 and will **receive critical bug-fixes only** (no new features). In 7.0.0 we can keep the [setup.py](https://github.com/giampaolo/psutil/blob/fbb6d9ce98f930d3d101b7df5a4f4d0f1d2b35a3/setup.py) script compatible with Python 2.7 in terms of syntax, so that it can emit an informative error message on pip install. E.g. the user will see something like this: ``` $ pip2 install psutil As of version 7.0.0 psutil no longer supports Python 2.7. Latest version supporting Python 2.7 is psutil 6.1.X. Install it with: "pip2 install psutil==6.1.*". ``` Related tickets: * 2017-06: https://github.com/giampaolo/psutil/issues/1053 * 2022-04: https://github.com/giampaolo/psutil/pull/2099 * 2023-04: https://github.com/giampaolo/psutil/pull/2246 * https://github.com/giampaolo/pyftpdlib/pull/635 --- .github/workflows/build.yml | 57 +-- .github/workflows/issues.py | 5 +- HISTORY.rst | 14 + INSTALL.rst | 1 - MANIFEST.in | 6 +- Makefile | 21 +- README.rst | 11 +- appveyor.yml | 83 --- docs/conf.py | 2 - docs/index.rst | 18 +- make.bat | 1 - psutil/__init__.py | 71 +-- psutil/_common.py | 128 ++--- psutil/_compat.py | 477 ------------------ psutil/_psaix.py | 25 +- psutil/_psbsd.py | 8 +- psutil/_pslinux.py | 170 ++----- psutil/_psosx.py | 2 - psutil/_psposix.py | 57 +-- psutil/_pssunos.py | 21 +- psutil/_psutil_aix.c | 28 +- psutil/_psutil_bsd.c | 46 +- psutil/_psutil_common.c | 9 - psutil/_psutil_common.h | 21 +- psutil/_psutil_linux.c | 51 +- psutil/_psutil_osx.c | 45 +- psutil/_psutil_posix.c | 47 +- psutil/_psutil_sunos.c | 27 +- psutil/_psutil_windows.c | 35 +- psutil/_pswindows.py | 155 ++---- psutil/arch/freebsd/proc.c | 4 - psutil/arch/linux/proc.c | 12 - psutil/arch/osx/disk.c | 6 - psutil/arch/windows/disk.c | 29 +- psutil/arch/windows/net.c | 11 +- psutil/arch/windows/proc.c | 4 - psutil/tests/__init__.py | 173 ++----- psutil/tests/test_bsd.py | 19 +- psutil/tests/test_connections.py | 5 +- psutil/tests/test_contracts.py | 22 +- psutil/tests/test_linux.py | 253 ++++------ psutil/tests/test_memleaks.py | 3 - psutil/tests/test_misc.py | 52 +- psutil/tests/test_posix.py | 9 +- psutil/tests/test_process.py | 70 +-- psutil/tests/test_process_all.py | 27 +- psutil/tests/test_system.py | 27 +- psutil/tests/test_testutils.py | 28 +- psutil/tests/test_unicode.py | 75 +-- psutil/tests/test_windows.py | 55 +- pyproject.toml | 24 +- scripts/battery.py | 1 - scripts/cpu_distribution.py | 5 +- scripts/fans.py | 1 - scripts/ifconfig.py | 1 - .../internal/appveyor_run_with_compiler.cmd | 89 ---- scripts/internal/bench_oneshot.py | 2 - scripts/internal/check_broken_links.py | 3 +- scripts/internal/clinter.py | 1 - ...ad_wheels_github.py => download_wheels.py} | 17 - scripts/internal/download_wheels_appveyor.py | 115 ----- scripts/internal/generate_manifest.py | 2 +- scripts/internal/git_pre_commit.py | 9 +- scripts/internal/install_pip.py | 12 +- scripts/internal/print_access_denied.py | 2 - scripts/internal/print_announce.py | 3 +- scripts/internal/print_api_speed.py | 2 - scripts/internal/print_downloads.py | 3 +- scripts/internal/print_timeline.py | 2 +- scripts/internal/test_python2_setup_py.py | 39 ++ scripts/internal/winmake.py | 31 +- scripts/iotop.py | 4 +- scripts/pidof.py | 1 - scripts/pmap.py | 10 +- scripts/procsmem.py | 1 - scripts/ps.py | 4 +- scripts/pstree.py | 1 - scripts/sensors.py | 2 - scripts/temperatures.py | 2 - setup.py | 85 ++-- 80 files changed, 709 insertions(+), 2291 deletions(-) delete mode 100644 appveyor.yml delete mode 100644 psutil/_compat.py delete mode 100644 scripts/internal/appveyor_run_with_compiler.cmd rename scripts/internal/{download_wheels_github.py => download_wheels.py} (78%) delete mode 100755 scripts/internal/download_wheels_appveyor.py create mode 100755 scripts/internal/test_python2_setup_py.py diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4975adc7a..f609ee651 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,8 +1,7 @@ # Runs CI tests and generates wheels on the following platforms: -# -# * Linux (py2 and py3) -# * macOS (py2 and py3) -# * Windows (py3, py2 is done by appveyor) +# * Linux +# * macOS +# * Windows # # Useful URLs: # * https://github.com/pypa/cibuildwheel @@ -16,9 +15,10 @@ concurrency: group: ${{ github.ref }}-${{ github.workflow }}-${{ github.event_name }}-${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && github.sha || '' }} cancel-in-progress: true jobs: - # Linux + macOS + Windows Python 3 - py3: - name: "py3, ${{ matrix.os }}, ${{ matrix.arch }}" + + # Run tests on Linux, macOS, Windows + tests: + name: "tests, ${{ matrix.os }}, ${{ matrix.arch }}" runs-on: ${{ matrix.os }} timeout-minutes: 30 strategy: @@ -64,7 +64,7 @@ jobs: - name: Upload wheels uses: actions/upload-artifact@v4 with: - name: wheels-py3-${{ matrix.os }}-${{ matrix.arch }} + name: wheels-${{ matrix.os }}-${{ matrix.arch }} path: wheelhouse - name: Generate .tar.gz @@ -74,42 +74,19 @@ jobs: python setup.py sdist mv dist/psutil*.tar.gz wheelhouse/ - # Linux + macOS + Python 2 - py2: - name: py2, ${{ matrix.os }} - runs-on: ${{ matrix.os }} + # Test python 2.7 fallback installation message produced by setup.py + py2-fallback: + name: py2.7 setup.py check + runs-on: ubuntu-24.04 timeout-minutes: 20 strategy: fail-fast: false - matrix: - os: [ubuntu-latest, macos-13] - env: - CIBW_BUILD: 'cp27-*' - CIBW_TEST_EXTRAS: test - CIBW_TEST_COMMAND: - make -C {project} PYTHON="env python" PSUTIL_SCRIPTS_DIR="{project}/scripts" install-sysdeps install-pydeps-test install print-sysinfo test test-memleaks - steps: - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 - with: - python-version: 3.9 - - - name: Create wheels + run tests - uses: pypa/cibuildwheel@v1.12.0 - - - name: Upload wheels - uses: actions/upload-artifact@v4 + - uses: LizardByte/setup-python-action@master with: - name: wheels-py2-${{ matrix.os }} - path: wheelhouse - - - name: Generate .tar.gz - if: matrix.os == 'ubuntu-latest' - run: | - make generate-manifest - python setup.py sdist - mv dist/psutil*.tar.gz wheelhouse/ + python-version: '2.7' + - run: python scripts/internal/test_python2_setup_py.py # Run linters linters: @@ -124,9 +101,9 @@ jobs: python3 -m pip install ruff black rstcheck toml-sort sphinx make lint-all - # upload weels as a single artefact + # Produce wheels as artifacts. upload-wheels: - needs: [py2, py3] + needs: [tests] runs-on: ubuntu-latest steps: - uses: actions/upload-artifact/merge@v4 diff --git a/.github/workflows/issues.py b/.github/workflows/issues.py index 9566fa119..5efe6fc32 100755 --- a/.github/workflows/issues.py +++ b/.github/workflows/issues.py @@ -9,7 +9,6 @@ on the situation. """ -from __future__ import print_function import functools import json @@ -42,7 +41,7 @@ "windows", "win32", "WinError", "WindowsError", "win10", "win7", "win ", "mingw", "msys", "studio", "microsoft", "make.bat", "CloseHandle", "GetLastError", "NtQuery", "DLL", "MSVC", "TCHAR", - "WCHAR", ".bat", "OpenProcess", "TerminateProcess", "appveyor", + "WCHAR", ".bat", "OpenProcess", "TerminateProcess", "windows error", "NtWow64", "NTSTATUS", "Visual Studio", ], "macos": [ @@ -89,7 +88,7 @@ ], # tests "tests": [ - " test ", "tests", "travis", "coverage", "cirrus", "appveyor", + " test ", "tests", "travis", "coverage", "cirrus", "continuous integration", "unittest", "pytest", "unit test", ], # critical errors diff --git a/HISTORY.rst b/HISTORY.rst index 99b7f0769..33df44fc6 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -1,5 +1,19 @@ *Bug tracker at https://github.com/giampaolo/psutil/issues* +7.0.0 (IN DEVELOPMENT) +====================== + +XXXX-XX-XX + +**Enhancements** + +- 2480_: Dropped Python 2.7 support. + +**Compatibility notes** + +- 2480_: Python 2.7 is no longer supported. Latest version supporting Python + 2.7 is psutil 6.1.X. Install it with: ``pip2 install psutil==6.1.*``. + 6.1.1 ===== diff --git a/INSTALL.rst b/INSTALL.rst index 8e06d400e..86972586c 100644 --- a/INSTALL.rst +++ b/INSTALL.rst @@ -116,7 +116,6 @@ Troubleshooting Install pip ----------- -Pip is shipped by default with Python 2.7.9+ and 3.4+. If you don't have pip you can install it with wget:: wget https://bootstrap.pypa.io/get-pip.py -O - | python3 diff --git a/MANIFEST.in b/MANIFEST.in index 91e5e2acc..5ec1cdd9e 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -24,7 +24,6 @@ include docs/requirements.txt include make.bat include psutil/__init__.py include psutil/_common.py -include psutil/_compat.py include psutil/_psaix.py include psutil/_psbsd.py include psutil/_pslinux.py @@ -177,14 +176,12 @@ include scripts/fans.py include scripts/free.py include scripts/ifconfig.py include scripts/internal/README -include scripts/internal/appveyor_run_with_compiler.cmd include scripts/internal/bench_oneshot.py include scripts/internal/bench_oneshot_2.py include scripts/internal/check_broken_links.py include scripts/internal/clinter.py include scripts/internal/convert_readme.py -include scripts/internal/download_wheels_appveyor.py -include scripts/internal/download_wheels_github.py +include scripts/internal/download_wheels.py include scripts/internal/generate_manifest.py include scripts/internal/git_pre_commit.py include scripts/internal/install-sysdeps.sh @@ -197,6 +194,7 @@ include scripts/internal/print_downloads.py include scripts/internal/print_hashes.py include scripts/internal/print_timeline.py include scripts/internal/purge_installation.py +include scripts/internal/test_python2_setup_py.py include scripts/internal/winmake.py include scripts/iotop.py include scripts/killall.py diff --git a/Makefile b/Makefile index 8ddb7c42c..2c7d050ce 100644 --- a/Makefile +++ b/Makefile @@ -6,13 +6,6 @@ PYTHON = python3 ARGS = -# "python3 setup.py build" can be parallelized on Python >= 3.6. -SETUP_BUILD_EXT_ARGS = `$(PYTHON) -c \ - "import sys, os; \ - py36 = sys.version_info[:2] >= (3, 6); \ - cpus = os.cpu_count() or 1 if py36 else 1; \ - print('--parallel %s' % cpus if cpus > 1 else '')"` - # In not in a virtualenv, add --user options for install commands. SETUP_INSTALL_ARGS = `$(PYTHON) -c \ "import sys; print('' if hasattr(sys, 'real_prefix') or hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix else '--user')"` @@ -55,6 +48,7 @@ clean: ## Remove all build files. dist/ \ docs/_build/ \ htmlcov/ \ + pytest-cache* \ wheelhouse .PHONY: build @@ -62,7 +56,7 @@ build: ## Compile (in parallel) without installing. @# "build_ext -i" copies compiled *.so files in ./psutil directory in order @# to allow "import psutil" when using the interactive interpreter from @# within this directory. - $(PYTHON_ENV_VARS) $(PYTHON) setup.py build_ext -i $(SETUP_BUILD_EXT_ARGS) + $(PYTHON_ENV_VARS) $(PYTHON) setup.py build_ext -i --parallel 4 $(PYTHON_ENV_VARS) $(PYTHON) -c "import psutil" # make sure it actually worked install: ## Install this package as current user in "edit" mode. @@ -224,12 +218,8 @@ sdist: ## Create tar.gz source distribution. ${MAKE} generate-manifest $(PYTHON_ENV_VARS) $(PYTHON) setup.py sdist -download-wheels-github: ## Download latest wheels hosted on github. - $(PYTHON_ENV_VARS) $(PYTHON) scripts/internal/download_wheels_github.py --tokenfile=~/.github.token - ${MAKE} print-dist - -download-wheels-appveyor: ## Download latest wheels hosted on appveyor. - $(PYTHON_ENV_VARS) $(PYTHON) scripts/internal/download_wheels_appveyor.py +download-wheels: ## Download latest wheels hosted on github. + $(PYTHON_ENV_VARS) $(PYTHON) scripts/internal/download_wheels.py --tokenfile=~/.github.token ${MAKE} print-dist create-wheels: ## Create .whl files @@ -265,8 +255,7 @@ pre-release: ## Check if we're ready to produce a new release. assert ver in doc, '%r not found in docs/index.rst' % ver; \ assert ver in history, '%r not found in HISTORY.rst' % ver; \ assert 'XXXX' not in history, 'XXXX found in HISTORY.rst';" - ${MAKE} download-wheels-github - ${MAKE} download-wheels-appveyor + ${MAKE} download-wheels ${MAKE} check-wheels ${MAKE} print-hashes ${MAKE} print-dist diff --git a/README.rst b/README.rst index 3fc6e601b..16c756c50 100644 --- a/README.rst +++ b/README.rst @@ -1,6 +1,6 @@ | |downloads| |stars| |forks| |contributors| |coverage| | |version| |py-versions| |packages| |license| -| |github-actions-wheels| |github-actions-bsd| |appveyor| |doc| |twitter| |tidelift| +| |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 @@ -26,10 +26,6 @@ :target: https://github.com/giampaolo/psutil/actions?query=workflow%3Absd-tests :alt: FreeBSD, NetBSD, OpenBSD -.. |appveyor| image:: https://img.shields.io/appveyor/build/giampaolo/psutil/master.svg?maxAge=3600&label=Windows%20(py2) - :target: https://ci.appveyor.com/project/giampaolo/psutil - :alt: Windows (Appveyor) - .. |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) @@ -98,8 +94,9 @@ psutil currently supports the following platforms: - **Sun Solaris** - **AIX** -Supported Python versions are **2.7**, **3.6+** and -`PyPy `__. +Supported Python versions are cPython 3.6+ and `PyPy `__. +Latest psutil version supporting Python 2.7 is +`psutil 6.1.1 `__. Funding ======= diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index 31f4de675..000000000 --- a/appveyor.yml +++ /dev/null @@ -1,83 +0,0 @@ -# Build: 4 (bump this up by 1 to force an appveyor run) - -os: Visual Studio 2015 -# avoid 2 builds when pushing on PRs -skip_branch_with_pr: true -# avoid build on new GIT tag -skip_tags: true -matrix: - # stop build on first failure - fast_finish: true -environment: - global: - # SDK v7.0 MSVC Express 2008's SetEnv.cmd script will fail if the - # /E:ON and /V:ON options are not enabled in the batch script interpreter - # See: http://stackoverflow.com/a/13751649/163740 - WITH_COMPILER: "cmd /E:ON /V:ON /C .\\scripts\\internal\\appveyor_run_with_compiler.cmd" - PYTHONWARNINGS: always - PYTHONUNBUFFERED: 1 - PSUTIL_DEBUG: 1 - matrix: - # 32 bits - - - PYTHON: "C:\\Python27" - PYTHON_VERSION: "2.7.x" - PYTHON_ARCH: "32" - - # 64 bits - - - PYTHON: "C:\\Python27-x64" - PYTHON_VERSION: "2.7.x" - PYTHON_ARCH: "64" - - -init: - - "ECHO %PYTHON% %PYTHON_VERSION% %PYTHON_ARCH%" - -install: - - "%WITH_COMPILER% %PYTHON%/python.exe -m pip --version" - - "%WITH_COMPILER% %PYTHON%/python.exe scripts/internal/winmake.py install-pydeps-test" - - "%WITH_COMPILER% %PYTHON%/python.exe -m pip freeze" - - "%WITH_COMPILER% %PYTHON%/python.exe scripts/internal/winmake.py install" - - "%WITH_COMPILER% %PYTHON%/python.exe scripts/internal/winmake.py print-sysinfo" - -build: off - -test_script: - - "%WITH_COMPILER% %PYTHON%/python.exe scripts/internal/winmake.py test" - - "%WITH_COMPILER% %PYTHON%/python.exe scripts/internal/winmake.py test-memleaks" - -after_test: - - "%WITH_COMPILER% %PYTHON%/python.exe scripts/internal/winmake.py wheel" - - "%WITH_COMPILER% %PYTHON%/python.exe scripts/internal/print_hashes.py dist" - - "%WITH_COMPILER% %PYTHON%/python.exe scripts/internal/print_access_denied.py" - - "%WITH_COMPILER% %PYTHON%/python.exe scripts/internal/print_api_speed.py" - -artifacts: - - path: dist\* - -cache: - - '%LOCALAPPDATA%\pip\Cache' - -# on_success: -# - might want to upload the content of dist/*.whl to a public wheelhouse - -skip_commits: - message: skip-appveyor - -# run build only if one of the following files is modified on commit -only_commits: - files: - - .ci/appveyor/* - - appveyor.yml - - psutil/__init__.py - - psutil/_common.py - - psutil/_compat.py - - psutil/_psutil_common.* - - psutil/_psutil_windows.* - - psutil/_pswindows.py - - psutil/arch/windows/* - - psutil/tests/* - - scripts/* - - scripts/internal/* - - setup.py diff --git a/docs/conf.py b/docs/conf.py index 3ebc64178..213aadfe0 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - # 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. diff --git a/docs/index.rst b/docs/index.rst index a9b9dcc66..443c46eed 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -224,8 +224,8 @@ CPU .. function:: cpu_count(logical=True) - Return the number of logical CPUs in the system (same as `os.cpu_count`_ - in Python 3.4) or ``None`` if undetermined. + Return the number of logical CPUs in the system (same as `os.cpu_count`_) + or ``None`` if undetermined. "logical CPUs" means the number of physical cores multiplied by the number of threads that can run on each core (this is known as Hyper Threading). If *logical* is ``False`` return the number of physical cores only, or @@ -1203,7 +1203,7 @@ Process class >>> import psutil >>> psutil.Process().exe() - '/usr/bin/python2.7' + '/usr/bin/python3' .. method:: cmdline() @@ -2637,6 +2637,18 @@ On Windows: set PSUTIL_DEBUG=1 python.exe script.py psutil-debug [psutil/arch/windows/proc.c:90]> NtWow64ReadVirtualMemory64(pbi64.PebBaseAddress) -> 998 (Unknown error) (ignored) +Python 2.7 +========== + +Latest version spporting Python 2.7 is `psutil 6.1.1 `__. +The 6.1.X serie may receive critical bug-fixes but no new features. It will +be maintained in the dedicated +`python2 `__ branch. +To install it: + +:: + + $ python2 -m pip install psutil==6.1.* Security ======== diff --git a/make.bat b/make.bat index ec4fc591e..97af61c3a 100644 --- a/make.bat +++ b/make.bat @@ -10,7 +10,6 @@ rem make install rem make test rem rem This script is modeled after my Windows installation which uses: -rem - Visual studio 2008 for Python 2.7 rem - Visual studio 2010 for Python 3.4+ rem ...therefore it might not work on your Windows installation. rem diff --git a/psutil/__init__.py b/psutil/__init__.py index 564833938..490e7b394 100644 --- a/psutil/__init__.py +++ b/psutil/__init__.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - # 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. @@ -17,10 +15,9 @@ - Sun Solaris - AIX -Works with Python versions 2.7 and 3.6+. +Supported Python versions are cPython 3.6+ and PyPy. """ -from __future__ import division import collections import contextlib @@ -28,6 +25,7 @@ import functools import os import signal +import socket import subprocess import sys import threading @@ -88,11 +86,6 @@ from ._common import debug from ._common import memoize_when_activated from ._common import wrap_numbers as _wrap_numbers -from ._compat import PY3 as _PY3 -from ._compat import PermissionError -from ._compat import ProcessLookupError -from ._compat import SubprocessTimeoutExpired as _SubprocessTimeoutExpired -from ._compat import long if LINUX: @@ -214,7 +207,7 @@ AF_LINK = _psplatform.AF_LINK __author__ = "Giampaolo Rodola'" -__version__ = "6.1.1" +__version__ = "7.0.0" version_info = tuple([int(num) for num in __version__.split('.')]) _timer = getattr(time, 'monotonic', time.time) @@ -287,7 +280,7 @@ def _pprint_secs(secs): # ===================================================================== -class Process(object): # noqa: UP004 +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. @@ -322,9 +315,6 @@ def _init(self, pid, _ignore_nsp=False): if pid is None: pid = os.getpid() else: - if not _PY3 and not isinstance(pid, (int, long)): - msg = "pid must be an integer (got %r)" % pid - raise TypeError(msg) if pid < 0: msg = "pid must be a positive integer (got %s)" % pid raise ValueError(msg) @@ -1358,11 +1348,12 @@ def wait(self, timeout=None): # The valid attr names which can be processed by Process.as_dict(). # fmt: off -_as_dict_attrnames = set( - [x for x in dir(Process) if not x.startswith('_') and x not in +_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', - 'memory_info_ex', 'connections', 'oneshot'}]) + 'memory_info_ex', 'connections', 'oneshot'} +} # fmt: on @@ -1448,7 +1439,7 @@ def __getattribute__(self, name): def wait(self, timeout=None): if self.__subproc.returncode is not None: return self.__subproc.returncode - ret = super(Popen, self).wait(timeout) # noqa + ret = super().wait(timeout) # noqa self.__subproc.returncode = ret return ret @@ -1586,9 +1577,7 @@ def wait_procs(procs, timeout=None, callback=None): def check_gone(proc, timeout): try: returncode = proc.wait(timeout=timeout) - except TimeoutExpired: - pass - except _SubprocessTimeoutExpired: + except (TimeoutExpired, subprocess.TimeoutExpired): pass else: if returncode is not None or not proc.is_running(): @@ -1646,7 +1635,7 @@ def check_gone(proc, timeout): def cpu_count(logical=True): """Return the number of logical CPUs in the system (same as - os.cpu_count() in Python 3.4). + os.cpu_count()). If *logical* is False return the number of physical cores only (e.g. hyper thread CPUs are excluded). @@ -2223,27 +2212,22 @@ def net_if_addrs(): Note: you can have more than one address of the same family associated with each interface. """ - has_enums = _PY3 - if has_enums: - import socket 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: - if has_enums: - 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 + 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: @@ -2405,8 +2389,9 @@ def _set_debug(value): def test(): # pragma: no cover + import shutil + from ._common import bytes2human - from ._compat import get_terminal_size today_day = datetime.date.today() # fmt: off @@ -2475,12 +2460,10 @@ def test(): # pragma: no cover cputime, cmdline, ) - print(line[: get_terminal_size()[0]]) # NOQA + print(line[: shutil.get_terminal_size()[0]]) # NOQA -del memoize_when_activated, division -if sys.version_info[0] < 3: - del num, x # noqa +del memoize_when_activated if __name__ == "__main__": test() diff --git a/psutil/_common.py b/psutil/_common.py index 4b99b093e..ffa6dcef3 100644 --- a/psutil/_common.py +++ b/psutil/_common.py @@ -7,12 +7,9 @@ # Note: this module is imported by setup.py so it should not import # psutil or third-party modules. -from __future__ import division -from __future__ import print_function import collections -import contextlib -import errno +import enum import functools import os import socket @@ -36,14 +33,6 @@ AF_UNIX = None -# can't take it from _common.py as this script is imported by setup.py -PY3 = sys.version_info[0] >= 3 -if PY3: - import enum -else: - enum = None - - PSUTIL_DEBUG = bool(os.getenv('PSUTIL_DEBUG')) _DEFAULT = object() @@ -57,7 +46,7 @@ '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', + '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', @@ -134,42 +123,29 @@ CONN_CLOSING = "CLOSING" CONN_NONE = "NONE" + # net_if_stats() -if enum is None: +class NicDuplex(enum.IntEnum): NIC_DUPLEX_FULL = 2 NIC_DUPLEX_HALF = 1 NIC_DUPLEX_UNKNOWN = 0 -else: - class NicDuplex(enum.IntEnum): - NIC_DUPLEX_FULL = 2 - NIC_DUPLEX_HALF = 1 - NIC_DUPLEX_UNKNOWN = 0 - globals().update(NicDuplex.__members__) +globals().update(NicDuplex.__members__) + # sensors_battery() -if enum is None: +class BatteryTime(enum.IntEnum): POWER_TIME_UNKNOWN = -1 POWER_TIME_UNLIMITED = -2 -else: - class BatteryTime(enum.IntEnum): - POWER_TIME_UNKNOWN = -1 - POWER_TIME_UNLIMITED = -2 - globals().update(BatteryTime.__members__) +globals().update(BatteryTime.__members__) # --- others ENCODING = sys.getfilesystemencoding() -if not PY3: - ENCODING_ERRS = "replace" -else: - try: - ENCODING_ERRS = sys.getfilesystemencodeerrors() # py 3.6 - except AttributeError: - ENCODING_ERRS = "surrogateescape" if POSIX else "replace" +ENCODING_ERRS = sys.getfilesystemencodeerrors() # =================================================================== @@ -391,26 +367,6 @@ def __reduce__(self): # =================================================================== -# This should be in _compat.py rather than here, but does not work well -# with setup.py importing this module via a sys.path trick. -if PY3: - if isinstance(__builtins__, dict): # cpython - exec_ = __builtins__["exec"] - else: # pypy - exec_ = getattr(__builtins__, "exec") # noqa - - 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 - - def usage_percent(used, total, round_=None): """Calculate percentage usage of 'used' against 'total'.""" try: @@ -456,7 +412,7 @@ def wrapper(*args, **kwargs): try: ret = cache[key] = fun(*args, **kwargs) except Exception as err: # noqa: BLE001 - raise raise_from(err, None) + raise err from None return ret def cache_clear(): @@ -505,14 +461,14 @@ def wrapper(self): try: return fun(self) except Exception as err: # noqa: BLE001 - raise raise_from(err, None) + 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 raise_from(err, None) + raise err from None try: self._cache[fun] = ret except AttributeError: @@ -546,9 +502,9 @@ def isfile_strict(path): """ try: st = os.stat(path) - except OSError as err: - if err.errno in {errno.EPERM, errno.EACCES}: - raise + except PermissionError: + raise + except OSError: return False else: return stat.S_ISREG(st.st_mode) @@ -561,9 +517,9 @@ def path_exists_strict(path): """ try: os.stat(path) - except OSError as err: - if err.errno in {errno.EPERM, errno.EACCES}: - raise + except PermissionError: + raise + except OSError: return False else: return True @@ -575,11 +531,10 @@ def supports_ipv6(): if not socket.has_ipv6 or AF_INET6 is None: return False try: - sock = socket.socket(AF_INET6, socket.SOCK_STREAM) - with contextlib.closing(sock): + with socket.socket(AF_INET6, socket.SOCK_STREAM) as sock: sock.bind(("::1", 0)) return True - except socket.error: + except OSError: return False @@ -615,26 +570,20 @@ 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. """ - if enum is None: + try: + return socket.AddressFamily(num) + except ValueError: return num - else: # pragma: no cover - 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. """ - if enum is None: + try: + return socket.SocketKind(num) + except ValueError: return num - else: # pragma: no cover - try: - return socket.SocketKind(num) - except ValueError: - return num def conn_to_ntuple(fd, fam, type_, laddr, raddr, status, status_map, pid=None): @@ -789,8 +738,6 @@ def wrap_numbers(input_dict, name): # 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 -# On Python 2 this also speeds up the reading of big files: -# (namely /proc/{pid}/smaps and /proc/net/*): # https://github.com/giampaolo/psutil/issues/708 FILE_READ_BUFFER_SIZE = 32 * 1024 @@ -800,13 +747,9 @@ def open_binary(fname): def open_text(fname): - """On Python 3 opens a file in text mode by using fs encoding and - a proper en/decoding errors handler. - On Python 2 this is just an alias for open(name, 'rt'). + """Open a file in text mode by using the proper FS encoding and + en/decoding error handlers. """ - if not PY3: - return open(fname, buffering=FILE_READ_BUFFER_SIZE) - # See: # https://github.com/giampaolo/psutil/issues/675 # https://github.com/giampaolo/psutil/pull/733 @@ -842,7 +785,7 @@ def cat(fname, fallback=_DEFAULT, _open=open_text): try: with _open(fname) as f: return f.read() - except (IOError, OSError): + except OSError: return fallback @@ -875,15 +818,8 @@ def get_procfs_path(): return sys.modules['psutil'].PROCFS_PATH -if PY3: - - def decode(s): - return s.decode(encoding=ENCODING, errors=ENCODING_ERRS) - -else: - - def decode(s): - return s +def decode(s): + return s.decode(encoding=ENCODING, errors=ENCODING_ERRS) # ===================================================================== @@ -984,7 +920,7 @@ def debug(msg): inspect.currentframe().f_back ) if isinstance(msg, Exception): - if isinstance(msg, (OSError, IOError, EnvironmentError)): + if isinstance(msg, OSError): # ...because str(exc) may contain info about the file name msg = "ignoring %s" % msg else: diff --git a/psutil/_compat.py b/psutil/_compat.py deleted file mode 100644 index 92e01713c..000000000 --- a/psutil/_compat.py +++ /dev/null @@ -1,477 +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. - -"""Module which provides compatibility with older Python versions. -This is more future-compatible rather than the opposite (prefer latest -Python 3 way of doing things). -""" - -import collections -import contextlib -import errno -import functools -import os -import sys -import types - - -# fmt: off -__all__ = [ - # constants - "PY3", - # builtins - "long", "range", "super", "unicode", "basestring", - # literals - "b", - # collections module - "lru_cache", - # shutil module - "which", "get_terminal_size", - # contextlib module - "redirect_stderr", - # python 3 exceptions - "FileNotFoundError", "PermissionError", "ProcessLookupError", - "InterruptedError", "ChildProcessError", "FileExistsError", -] -# fmt: on - - -PY3 = sys.version_info[0] >= 3 -_SENTINEL = object() - -if PY3: - long = int - xrange = range - unicode = str - basestring = str - range = range - - def b(s): - return s.encode("latin-1") - -else: - long = long - range = xrange - unicode = unicode - basestring = basestring - - def b(s): - return s - - -# --- builtins - - -# Python 3 super(). -# Taken from "future" package. -# Credit: Ryan Kelly -if PY3: - super = super -else: - _builtin_super = super - - def super(type_=_SENTINEL, type_or_obj=_SENTINEL, framedepth=1): - """Like Python 3 builtin super(). If called without any arguments - it attempts to infer them at runtime. - """ - if type_ is _SENTINEL: - f = sys._getframe(framedepth) - try: - # Get the function's first positional argument. - type_or_obj = f.f_locals[f.f_code.co_varnames[0]] - except (IndexError, KeyError): - msg = 'super() used in a function with no args' - raise RuntimeError(msg) - try: - # Get the MRO so we can crawl it. - mro = type_or_obj.__mro__ - except (AttributeError, RuntimeError): - try: - mro = type_or_obj.__class__.__mro__ - except AttributeError: - msg = 'super() used in a non-newstyle class' - raise RuntimeError(msg) - for type_ in mro: - # Find the class that owns the currently-executing method. - for meth in type_.__dict__.values(): - # Drill down through any wrappers to the underlying func. - # This handles e.g. classmethod() and staticmethod(). - try: - while not isinstance(meth, types.FunctionType): - if isinstance(meth, property): - # Calling __get__ on the property will invoke - # user code which might throw exceptions or - # have side effects - meth = meth.fget - else: - try: - meth = meth.__func__ - except AttributeError: - meth = meth.__get__(type_or_obj, type_) - except (AttributeError, TypeError): - continue - if meth.func_code is f.f_code: - break # found - else: - # Not found. Move onto the next class in MRO. - continue - break # found - else: - msg = 'super() called outside a method' - raise RuntimeError(msg) - - # Dispatch to builtin super(). - if type_or_obj is not _SENTINEL: - return _builtin_super(type_, type_or_obj) - return _builtin_super(type_) - - -# --- exceptions - - -if PY3: - FileNotFoundError = FileNotFoundError # NOQA - PermissionError = PermissionError # NOQA - ProcessLookupError = ProcessLookupError # NOQA - InterruptedError = InterruptedError # NOQA - ChildProcessError = ChildProcessError # NOQA - FileExistsError = FileExistsError # NOQA -else: - # https://github.com/PythonCharmers/python-future/blob/exceptions/ - # src/future/types/exceptions/pep3151.py - import platform - - def _instance_checking_exception(base_exception=Exception): - def wrapped(instance_checker): - class TemporaryClass(base_exception): - def __init__(self, *args, **kwargs): - if len(args) == 1 and isinstance(args[0], TemporaryClass): - unwrap_me = args[0] - for attr in dir(unwrap_me): - if not attr.startswith('__'): - setattr(self, attr, getattr(unwrap_me, attr)) - else: - super(TemporaryClass, self).__init__( # noqa - *args, **kwargs - ) - - class __metaclass__(type): - def __instancecheck__(cls, inst): - return instance_checker(inst) - - def __subclasscheck__(cls, classinfo): - value = sys.exc_info()[1] - return isinstance(value, cls) - - TemporaryClass.__name__ = instance_checker.__name__ - TemporaryClass.__doc__ = instance_checker.__doc__ - return TemporaryClass - - return wrapped - - @_instance_checking_exception(EnvironmentError) - def FileNotFoundError(inst): - return getattr(inst, 'errno', _SENTINEL) == errno.ENOENT - - @_instance_checking_exception(EnvironmentError) - def ProcessLookupError(inst): - return getattr(inst, 'errno', _SENTINEL) == errno.ESRCH - - @_instance_checking_exception(EnvironmentError) - def PermissionError(inst): - return getattr(inst, 'errno', _SENTINEL) in {errno.EACCES, errno.EPERM} - - @_instance_checking_exception(EnvironmentError) - def InterruptedError(inst): - return getattr(inst, 'errno', _SENTINEL) == errno.EINTR - - @_instance_checking_exception(EnvironmentError) - def ChildProcessError(inst): - return getattr(inst, 'errno', _SENTINEL) == errno.ECHILD - - @_instance_checking_exception(EnvironmentError) - def FileExistsError(inst): - return getattr(inst, 'errno', _SENTINEL) == errno.EEXIST - - if platform.python_implementation() != "CPython": - try: - raise OSError(errno.EEXIST, "perm") - except FileExistsError: - pass - except OSError: - msg = ( - "broken or incompatible Python implementation, see: " - "https://github.com/giampaolo/psutil/issues/1659" - ) - raise RuntimeError(msg) - - -# --- stdlib additions - - -# py 3.2 functools.lru_cache -# Taken from: http://code.activestate.com/recipes/578078 -# Credit: Raymond Hettinger -try: - from functools import lru_cache -except ImportError: - try: - from threading import RLock - except ImportError: - from dummy_threading import RLock - - _CacheInfo = collections.namedtuple( - "CacheInfo", ["hits", "misses", "maxsize", "currsize"] - ) - - class _HashedSeq(list): # noqa: FURB189 - __slots__ = ('hashvalue',) - - def __init__(self, tup, hash=hash): - self[:] = tup - self.hashvalue = hash(tup) - - def __hash__(self): - return self.hashvalue - - def _make_key( - args, - kwds, - typed, - kwd_mark=(_SENTINEL,), - fasttypes=set((int, str, frozenset, type(None))), # noqa - sorted=sorted, - tuple=tuple, - type=type, - len=len, - ): - key = args - if kwds: - sorted_items = sorted(kwds.items()) - key += kwd_mark - for item in sorted_items: - key += item - if typed: - key += tuple(type(v) for v in args) - if kwds: - key += tuple(type(v) for k, v in sorted_items) - elif len(key) == 1 and type(key[0]) in fasttypes: - return key[0] - return _HashedSeq(key) - - def lru_cache(maxsize=100, typed=False): - """Least-recently-used cache decorator, see: - http://docs.python.org/3/library/functools.html#functools.lru_cache. - """ - - def decorating_function(user_function): - cache = {} - stats = [0, 0] - HITS, MISSES = 0, 1 - make_key = _make_key - cache_get = cache.get - _len = len - lock = RLock() - root = [] - root[:] = [root, root, None, None] - nonlocal_root = [root] - PREV, NEXT, KEY, RESULT = 0, 1, 2, 3 - if maxsize == 0: - - def wrapper(*args, **kwds): - result = user_function(*args, **kwds) - stats[MISSES] += 1 - return result - - elif maxsize is None: - - def wrapper(*args, **kwds): - key = make_key(args, kwds, typed) - result = cache_get(key, root) - if result is not root: - stats[HITS] += 1 - return result - result = user_function(*args, **kwds) - cache[key] = result - stats[MISSES] += 1 - return result - - else: - - def wrapper(*args, **kwds): - if kwds or typed: - key = make_key(args, kwds, typed) - else: - key = args - lock.acquire() - try: - link = cache_get(key) - if link is not None: - (root,) = nonlocal_root - link_prev, link_next, key, result = link - link_prev[NEXT] = link_next - link_next[PREV] = link_prev - last = root[PREV] - last[NEXT] = root[PREV] = link - link[PREV] = last - link[NEXT] = root - stats[HITS] += 1 - return result - finally: - lock.release() - result = user_function(*args, **kwds) - lock.acquire() - try: - (root,) = nonlocal_root - if key in cache: - pass - elif _len(cache) >= maxsize: - oldroot = root - oldroot[KEY] = key - oldroot[RESULT] = result - root = nonlocal_root[0] = oldroot[NEXT] - oldkey = root[KEY] - root[KEY] = root[RESULT] = None - del cache[oldkey] - cache[key] = oldroot - else: - last = root[PREV] - link = [last, root, key, result] - last[NEXT] = root[PREV] = cache[key] = link - stats[MISSES] += 1 - finally: - lock.release() - return result - - def cache_info(): - """Report cache statistics.""" - lock.acquire() - try: - return _CacheInfo( - stats[HITS], stats[MISSES], maxsize, len(cache) - ) - finally: - lock.release() - - def cache_clear(): - """Clear the cache and cache statistics.""" - lock.acquire() - try: - cache.clear() - root = nonlocal_root[0] - root[:] = [root, root, None, None] - stats[:] = [0, 0] - finally: - lock.release() - - wrapper.__wrapped__ = user_function - wrapper.cache_info = cache_info - wrapper.cache_clear = cache_clear - return functools.update_wrapper(wrapper, user_function) - - return decorating_function - - -# python 3.3 -try: - from shutil import which -except ImportError: - - def which(cmd, mode=os.F_OK | os.X_OK, path=None): - """Given a command, mode, and a PATH string, return the path which - conforms to the given mode on the PATH, or None if there is no such - file. - - `mode` defaults to os.F_OK | os.X_OK. `path` defaults to the result - of os.environ.get("PATH"), or can be overridden with a custom search - path. - """ - - def _access_check(fn, mode): - return ( - os.path.exists(fn) - and os.access(fn, mode) - and not os.path.isdir(fn) - ) - - if os.path.dirname(cmd): - if _access_check(cmd, mode): - return cmd - return None - - if path is None: - path = os.environ.get("PATH", os.defpath) - if not path: - return None - path = path.split(os.pathsep) - - if sys.platform == "win32": - if os.curdir not in path: - path.insert(0, os.curdir) - - pathext = os.environ.get("PATHEXT", "").split(os.pathsep) - if any(cmd.lower().endswith(ext.lower()) for ext in pathext): - files = [cmd] - else: - files = [cmd + ext for ext in pathext] - else: - files = [cmd] - - seen = set() - for dir in path: - normdir = os.path.normcase(dir) - if normdir not in seen: - seen.add(normdir) - for thefile in files: - name = os.path.join(dir, thefile) - if _access_check(name, mode): - return name - return None - - -# python 3.3 -try: - from shutil import get_terminal_size -except ImportError: - - def get_terminal_size(fallback=(80, 24)): - try: - import fcntl - import struct - import termios - except ImportError: - return fallback - else: - try: - # This should work on Linux. - res = struct.unpack( - 'hh', fcntl.ioctl(1, termios.TIOCGWINSZ, '1234') - ) - return (res[1], res[0]) - except Exception: # noqa: BLE001 - return fallback - - -# python 3.3 -try: - from subprocess import TimeoutExpired as SubprocessTimeoutExpired -except ImportError: - - class SubprocessTimeoutExpired(Exception): - pass - - -# python 3.5 -try: - from contextlib import redirect_stderr -except ImportError: - - @contextlib.contextmanager - def redirect_stderr(new_target): - original = sys.stderr - try: - sys.stderr = new_target - yield new_target - finally: - sys.stderr = original diff --git a/psutil/_psaix.py b/psutil/_psaix.py index 2ccc638bc..94a07a3a5 100644 --- a/psutil/_psaix.py +++ b/psutil/_psaix.py @@ -28,10 +28,6 @@ from ._common import get_procfs_path from ._common import memoize_when_activated from ._common import usage_percent -from ._compat import PY3 -from ._compat import FileNotFoundError -from ._compat import PermissionError -from ._compat import ProcessLookupError __extra__all__ = ["PROCFS_PATH"] @@ -148,10 +144,7 @@ def cpu_count_cores(): cmd = ["lsdev", "-Cc", "processor"] p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = p.communicate() - if PY3: - stdout, stderr = ( - x.decode(sys.stdout.encoding) for x in (stdout, stderr) - ) + stdout, stderr = (x.decode(sys.stdout.encoding) for x in (stdout, stderr)) if p.returncode != 0: raise RuntimeError("%r command error\n%s" % (cmd, stderr)) processors = stdout.strip().splitlines() @@ -243,7 +236,7 @@ def net_connections(kind, _pid=-1): def net_if_stats(): """Get NIC stats (isup, duplex, speed, mtu).""" duplex_map = {"Full": NIC_DUPLEX_FULL, "Half": NIC_DUPLEX_HALF} - names = set([x[0] for x in net_if_addrs()]) + names = {x[0] for x in net_if_addrs()} ret = {} for name in names: mtu = cext_posix.net_if_mtu(name) @@ -260,10 +253,9 @@ def net_if_stats(): stderr=subprocess.PIPE, ) stdout, stderr = p.communicate() - if PY3: - stdout, stderr = ( - x.decode(sys.stdout.encoding) for x in (stdout, stderr) - ) + 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 @@ -533,10 +525,9 @@ def open_files(self): stderr=subprocess.PIPE, ) stdout, stderr = p.communicate() - if PY3: - stdout, stderr = ( - x.decode(sys.stdout.encoding) for x in (stdout, stderr) - ) + 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) diff --git a/psutil/_psbsd.py b/psutil/_psbsd.py index 77b99c0fd..209e5476c 100644 --- a/psutil/_psbsd.py +++ b/psutil/_psbsd.py @@ -28,10 +28,6 @@ from ._common import memoize from ._common import memoize_when_activated from ._common import usage_percent -from ._compat import FileNotFoundError -from ._compat import PermissionError -from ._compat import ProcessLookupError -from ._compat import which __extra__all__ = [] @@ -690,9 +686,11 @@ def exe(self): # 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 which(cmdline[0]) or "" + return shutil.which(cmdline[0]) or "" else: return "" diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py index c9c97f6ec..5d5595feb 100644 --- a/psutil/_pslinux.py +++ b/psutil/_pslinux.py @@ -4,15 +4,16 @@ """Linux platform implementation.""" -from __future__ import division import base64 import collections +import enum import errno import functools import glob import os import re +import resource import socket import struct import sys @@ -24,6 +25,7 @@ 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 @@ -44,18 +46,6 @@ from ._common import path_exists_strict from ._common import supports_ipv6 from ._common import usage_percent -from ._compat import PY3 -from ._compat import FileNotFoundError -from ._compat import PermissionError -from ._compat import ProcessLookupError -from ._compat import b -from ._compat import basestring - - -if PY3: - import enum -else: - enum = None # fmt: off @@ -69,6 +59,11 @@ "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 @@ -102,29 +97,21 @@ # * https://lkml.org/lkml/2015/8/17/234 DISK_SECTOR_SIZE = 512 -if enum is None: - AF_LINK = socket.AF_PACKET -else: - AddressFamily = enum.IntEnum( - 'AddressFamily', {'AF_LINK': int(socket.AF_PACKET)} - ) - AF_LINK = AddressFamily.AF_LINK +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 -if enum is None: +class IOPriority(enum.IntEnum): IOPRIO_CLASS_NONE = 0 IOPRIO_CLASS_RT = 1 IOPRIO_CLASS_BE = 2 IOPRIO_CLASS_IDLE = 3 -else: - class IOPriority(enum.IntEnum): - IOPRIO_CLASS_NONE = 0 - IOPRIO_CLASS_RT = 1 - IOPRIO_CLASS_BE = 2 - IOPRIO_CLASS_IDLE = 3 - globals().update(IOPriority.__members__) +globals().update(IOPriority.__members__) # See: # https://github.com/torvalds/linux/blame/master/fs/proc/array.c @@ -211,7 +198,7 @@ class IOPriority(enum.IntEnum): def readlink(path): """Wrapper around os.readlink().""" - assert isinstance(path, basestring), path + 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 @@ -294,58 +281,6 @@ def set_scputimes_ntuple(procfs_path): scputimes = namedtuple('scputimes', 'user system idle')(0.0, 0.0, 0.0) -# ===================================================================== -# --- prlimit -# ===================================================================== - -# Backport of resource.prlimit() for Python 2. Originally this was done -# in C, but CentOS-6 which we use to create manylinux wheels is too old -# and does not support prlimit() syscall. As such the resulting wheel -# would not include prlimit(), even when installed on newer systems. -# This is the only part of psutil using ctypes. - -prlimit = None -try: - from resource import prlimit # python >= 3.4 -except ImportError: - import ctypes - - libc = ctypes.CDLL(None, use_errno=True) - - if hasattr(libc, "prlimit"): - - def prlimit(pid, resource_, limits=None): - class StructRlimit(ctypes.Structure): - _fields_ = [ - ('rlim_cur', ctypes.c_longlong), - ('rlim_max', ctypes.c_longlong), - ] - - current = StructRlimit() - if limits is None: - # get - ret = libc.prlimit(pid, resource_, None, ctypes.byref(current)) - else: - # set - new = StructRlimit() - new.rlim_cur = limits[0] - new.rlim_max = limits[1] - ret = libc.prlimit( - pid, resource_, ctypes.byref(new), ctypes.byref(current) - ) - - if ret != 0: - errno_ = ctypes.get_errno() - raise OSError(errno_, os.strerror(errno_)) - return (current.rlim_cur, current.rlim_max) - - -if prlimit is not None: - __extra__all__.extend( - [x for x in dir(cext) if x.startswith('RLIM') and x.isupper()] - ) - - # ===================================================================== # --- system memory # ===================================================================== @@ -396,7 +331,7 @@ def calculate_avail_vmem(mems): return fallback try: f = open_binary('%s/zoneinfo' % get_procfs_path()) - except IOError: + except OSError: return fallback # kernel 2.6.13 watermark_low = 0 @@ -572,7 +507,7 @@ def swap_memory(): # get pgin/pgouts try: f = open_binary("%s/vmstat" % get_procfs_path()) - except IOError as err: + except OSError as err: # see https://github.com/giampaolo/psutil/issues/722 msg = ( "'sin' and 'sout' swap memory stats couldn't " @@ -778,9 +713,7 @@ def cpu_freq(): # https://github.com/giampaolo/psutil/issues/1071 curr = bcat(pjoin(path, "cpuinfo_cur_freq"), fallback=None) if curr is None: - online_path = ( - "/sys/devices/system/cpu/cpu{}/online".format(i) - ) + 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)) @@ -914,8 +847,7 @@ def decode_address(addr, family): # no end-points connected if not port: return () - if PY3: - ip = ip.encode('ascii') + ip = ip.encode('ascii') if family == socket.AF_INET: # see: https://github.com/giampaolo/psutil/issues/201 if LITTLE_ENDIAN: @@ -1311,17 +1243,17 @@ def find(self): if path is None: try: path = self.ask_proc_partitions() - except (IOError, OSError) as err: + except OSError as err: debug(err) if path is None: try: path = self.ask_sys_dev_block() - except (IOError, OSError) as err: + except OSError as err: debug(err) if path is None: try: path = self.ask_sys_class_block() - except (IOError, OSError) as err: + 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. @@ -1392,7 +1324,7 @@ def sensors_temperatures(): # 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(set([x.split('_')[0] for x in basenames])) + basenames = sorted({x.split('_')[0] for x in basenames}) # Only add the coretemp hwmon entries if they're not already in # /sys/class/hwmon/ @@ -1413,7 +1345,7 @@ def sensors_temperatures(): current = float(bcat(path)) / 1000.0 path = os.path.join(os.path.dirname(base), 'name') unit_name = cat(path).strip() - except (IOError, OSError, ValueError): + 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. @@ -1452,15 +1384,15 @@ def sensors_temperatures(): current = float(bcat(path)) / 1000.0 path = os.path.join(base, 'type') unit_name = cat(path).strip() - except (IOError, OSError, ValueError) as err: + except (OSError, ValueError) as err: debug(err) continue trip_paths = glob.glob(base + '/trip_point*') - trip_points = set([ + 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: @@ -1508,11 +1440,11 @@ def sensors_fans(): # https://github.com/giampaolo/psutil/issues/971 basenames = glob.glob('/sys/class/hwmon/hwmon*/device/fan*_*') - basenames = sorted(set([x.split('_')[0] for x in basenames])) + basenames = sorted({x.split("_")[0] for x in basenames}) for base in basenames: try: current = int(bcat(base + '_input')) - except (IOError, OSError) as err: + except OSError as err: debug(err) continue unit_name = cat(os.path.join(os.path.dirname(base), 'name')).strip() @@ -1648,7 +1580,8 @@ def boot_time(): def pids(): """Returns a list of PIDs currently running on the system.""" - return [int(x) for x in os.listdir(b(get_procfs_path())) if x.isdigit()] + path = get_procfs_path().encode(ENCODING) + return [int(x) for x in os.listdir(path) if x.isdigit()] def pid_exists(pid): @@ -1679,7 +1612,7 @@ def pid_exists(pid): # dealing with a process PID. return tgid == pid raise ValueError("'Tgid' line not found in %s" % path) - except (EnvironmentError, ValueError): + except (OSError, ValueError): return pid in pids() @@ -1706,7 +1639,7 @@ def ppid_map(): def wrap_exceptions(fun): - """Decorator which translates bare OSError and IOError exceptions + """Decorator which translates bare OSError and OSError exceptions into NoSuchProcess and AccessDenied. """ @@ -1753,7 +1686,7 @@ def _is_zombie(self): # exception. try: data = bcat("%s/%s/stat" % (self._procfs_path, self.pid)) - except (IOError, OSError): + except OSError: return False else: rpar = data.rfind(b')') @@ -1837,11 +1770,8 @@ def oneshot_exit(self): @wrap_exceptions def name(self): - name = self._parse_stat_file()['name'] - if PY3: - name = decode(name) # XXX - gets changed later and probably needs refactoring - return name + return decode(self._parse_stat_file()['name']) @wrap_exceptions def exe(self): @@ -1995,7 +1925,7 @@ def _parse_smaps_rollup(self): # compared to /proc/pid/smaps_rollup. uss = pss = swap = 0 with open_binary( - "{}/{}/smaps_rollup".format(self._procfs_path, self.pid) + f"{self._procfs_path}/{self.pid}/smaps_rollup" ) as f: for line in f: if line.startswith(b"Private_"): @@ -2020,8 +1950,6 @@ def _parse_smaps( # Note: using 3 regexes is faster than reading the file # line by line. - # XXX: on Python 3 the 2 regexes are 30% slower than on - # Python 2 though. Figure out why. # # You might be tempted to calculate USS by subtracting # the "shared" value from the "resident" value in @@ -2106,8 +2034,7 @@ def get_blocks(lines, current_block): if not path: path = '[anon]' else: - if PY3: - path = decode(path) + path = decode(path) path = path.strip() if path.endswith(' (deleted)') and not path_exists_strict( path @@ -2152,9 +2079,7 @@ def num_ctx_switches( @wrap_exceptions def num_threads(self, _num_threads_re=re.compile(br'Threads:\t(\d+)')): - # Note: on Python 3 using a re is faster than iterating over file - # line by line. On Python 2 is the exact opposite, and iterating - # over a file on Python 3 is slower than on Python 2. + # 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]) @@ -2247,22 +2172,24 @@ def cpu_affinity_set(self, cpus): @wrap_exceptions def ionice_get(self): ioclass, value = cext.proc_ioprio_get(self.pid) - if enum is not None: - ioclass = IOPriority(ioclass) + 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 {IOPRIO_CLASS_IDLE, IOPRIO_CLASS_NONE}: + if value and ioclass in { + IOPriority.IOPRIO_CLASS_IDLE, + IOPriority.IOPRIO_CLASS_NONE, + }: raise ValueError("%r ioclass accepts no value" % ioclass) 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 prlimit is not None: + if hasattr(resource, "prlimit"): @wrap_exceptions def rlimit(self, resource_, limits=None): @@ -2275,7 +2202,7 @@ def rlimit(self, resource_, limits=None): try: if limits is None: # get - return prlimit(self.pid, resource_) + return resource.prlimit(self.pid, resource_) else: # set if len(limits) != 2: @@ -2284,7 +2211,7 @@ def rlimit(self, resource_, limits=None): + "tuple, got %s" % repr(limits) ) raise ValueError(msg) - prlimit(self.pid, resource_, limits) + resource.prlimit(self.pid, resource_, limits) except OSError as err: if err.errno == errno.ENOSYS: # I saw this happening on Travis: @@ -2295,8 +2222,7 @@ def rlimit(self, resource_, limits=None): @wrap_exceptions def status(self): letter = self._parse_stat_file()['status'] - if PY3: - letter = letter.decode() + letter = letter.decode() # XXX is '?' legit? (we're not supposed to return it anyway) return PROC_STATUSES.get(letter, '?') diff --git a/psutil/_psosx.py b/psutil/_psosx.py index ed9b319de..c6078ea3d 100644 --- a/psutil/_psosx.py +++ b/psutil/_psosx.py @@ -22,8 +22,6 @@ from ._common import memoize_when_activated from ._common import parse_environ_block from ._common import usage_percent -from ._compat import PermissionError -from ._compat import ProcessLookupError __extra__all__ = [] diff --git a/psutil/_psposix.py b/psutil/_psposix.py index 42bdfa7ef..e07481986 100644 --- a/psutil/_psposix.py +++ b/psutil/_psposix.py @@ -4,10 +4,10 @@ """Routines common to all posix systems.""" +import enum import glob import os import signal -import sys import time from ._common import MACOS @@ -15,25 +15,12 @@ from ._common import memoize from ._common import sdiskusage from ._common import usage_percent -from ._compat import PY3 -from ._compat import ChildProcessError -from ._compat import FileNotFoundError -from ._compat import InterruptedError -from ._compat import PermissionError -from ._compat import ProcessLookupError -from ._compat import unicode if MACOS: from . import _psutil_osx -if PY3: - import enum -else: - enum = None - - __all__ = ['pid_exists', 'wait_pid', 'disk_usage', 'get_terminal_map'] @@ -59,23 +46,16 @@ def pid_exists(pid): return True -# Python 3.5 signals enum (contributed by me ^^): -# https://bugs.python.org/issue21076 -if enum is not None and hasattr(signal, "Signals"): - Negsignal = enum.IntEnum( - 'Negsignal', dict([(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 +Negsignal = enum.IntEnum( + 'Negsignal', {x.name: -x.value for x in signal.Signals} +) -else: # pragma: no cover - def negsig_to_enum(num): +def negsig_to_enum(num): + """Convert a negative signal value to an enum.""" + try: + return Negsignal(num) + except ValueError: return num @@ -181,24 +161,7 @@ def disk_usage(path): total and used disk space whereas "free" and "percent" represent the "free" and "used percent" user disk space. """ - if PY3: - st = os.statvfs(path) - else: # pragma: no cover - # os.statvfs() does not support unicode on Python 2: - # - https://github.com/giampaolo/psutil/issues/416 - # - http://bugs.python.org/issue18695 - try: - st = os.statvfs(path) - except UnicodeEncodeError: - if isinstance(path, unicode): - try: - path = path.encode(sys.getfilesystemencoding()) - except UnicodeEncodeError: - pass - st = os.statvfs(path) - else: - raise - + st = os.statvfs(path) # Total space which is only available to root (unless changed # at system level). total = st.f_blocks * st.f_frsize diff --git a/psutil/_pssunos.py b/psutil/_pssunos.py index a38d939d7..09a79fef4 100644 --- a/psutil/_pssunos.py +++ b/psutil/_pssunos.py @@ -18,6 +18,7 @@ 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 @@ -28,11 +29,6 @@ from ._common import sockfam_to_enum from ._common import socktype_to_enum from ._common import usage_percent -from ._compat import PY3 -from ._compat import FileNotFoundError -from ._compat import PermissionError -from ._compat import ProcessLookupError -from ._compat import b __extra__all__ = ["CONN_IDLE", "CONN_BOUND", "PROCFS_PATH"] @@ -154,8 +150,7 @@ def swap_memory(): stdout=subprocess.PIPE, ) stdout, _ = p.communicate() - if PY3: - stdout = stdout.decode(sys.stdout.encoding) + stdout = stdout.decode(sys.stdout.encoding) if p.returncode != 0: raise RuntimeError("'swap -l' failed (retcode=%s)" % p.returncode) @@ -346,7 +341,8 @@ def users(): def pids(): """Returns a list of PIDs currently running on the system.""" - return [int(x) for x in os.listdir(b(get_procfs_path())) if x.isdigit()] + path = get_procfs_path().encode(ENCODING) + return [int(x) for x in os.listdir(path) if x.isdigit()] def pid_exists(pid): @@ -588,7 +584,7 @@ def threads(self): utime, stime = cext.query_process_thread( self.pid, tid, procfs_path ) - except EnvironmentError as err: + 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. @@ -640,10 +636,9 @@ def _get_unix_sockets(self, pid): cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) stdout, stderr = p.communicate() - if PY3: - stdout, stderr = ( - x.decode(sys.stdout.encoding) for x in (stdout, stderr) - ) + 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) diff --git a/psutil/_psutil_aix.c b/psutil/_psutil_aix.c index 42f921188..958a2c0c2 100644 --- a/psutil/_psutil_aix.c +++ b/psutil/_psutil_aix.c @@ -58,6 +58,7 @@ #define TV2DOUBLE(t) (((t).tv_nsec * 0.000000001) + (t).tv_sec) +#define INITERROR return NULL /* * Read a file content and fills a C structure with it. @@ -1030,18 +1031,12 @@ struct module_state { PyObject *error; }; -#if PY_MAJOR_VERSION >= 3 #define GETSTATE(m) ((struct module_state*)PyModule_GetState(m)) -#else -#define GETSTATE(m) (&_state) -#endif #ifdef __cplusplus extern "C" { #endif -#if PY_MAJOR_VERSION >= 3 - static int psutil_aix_traverse(PyObject *m, visitproc visit, void *arg) { Py_VISIT(GETSTATE(m)->error); @@ -1066,21 +1061,13 @@ static struct PyModuleDef moduledef = { NULL }; -#define INITERROR return NULL -PyMODINIT_FUNC PyInit__psutil_aix(void) - -#else -#define INITERROR return - -void init_psutil_aix(void) -#endif -{ -#if PY_MAJOR_VERSION >= 3 +PyMODINIT_FUNC +PyInit__psutil_aix(void) { PyObject *module = PyModule_Create(&moduledef); -#else - PyObject *module = Py_InitModule("_psutil_aix", PsutilMethods); -#endif + if (module == NULL) + INITERROR; + #ifdef Py_GIL_DISABLED PyUnstable_Module_SetGIL(mod, Py_MOD_GIL_NOT_USED); #endif @@ -1109,9 +1096,8 @@ void init_psutil_aix(void) if (module == NULL) INITERROR; -#if PY_MAJOR_VERSION >= 3 + return module; -#endif } #ifdef __cplusplus diff --git a/psutil/_psutil_bsd.c b/psutil/_psutil_bsd.c index facaba831..503e96cac 100644 --- a/psutil/_psutil_bsd.c +++ b/psutil/_psutil_bsd.c @@ -52,6 +52,9 @@ #endif +#define INITERR return NULL + + /* * define the psutil C module methods and initialize the module. */ @@ -112,34 +115,23 @@ static PyMethodDef mod_methods[] = { {NULL, NULL, 0, NULL} }; -#if PY_MAJOR_VERSION >= 3 - #define INITERR return NULL - - static struct PyModuleDef moduledef = { - PyModuleDef_HEAD_INIT, - "_psutil_bsd", - NULL, - -1, - mod_methods, - NULL, - NULL, - NULL, - NULL - }; - - PyObject *PyInit__psutil_bsd(void) -#else /* PY_MAJOR_VERSION */ - #define INITERR return - - void init_psutil_bsd(void) -#endif /* PY_MAJOR_VERSION */ -{ + +static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + "_psutil_bsd", + NULL, + -1, + mod_methods, + NULL, + NULL, + NULL, + NULL +}; + +PyObject +*PyInit__psutil_bsd(void) { PyObject *v; -#if PY_MAJOR_VERSION >= 3 PyObject *mod = PyModule_Create(&moduledef); -#else - PyObject *mod = Py_InitModule("_psutil_bsd", mod_methods); -#endif if (mod == NULL) INITERR; @@ -210,7 +202,5 @@ static PyMethodDef mod_methods[] = { if (mod == NULL) INITERR; -#if PY_MAJOR_VERSION >= 3 return mod; -#endif } diff --git a/psutil/_psutil_common.c b/psutil/_psutil_common.c index d16816972..81132fd3e 100644 --- a/psutil/_psutil_common.c +++ b/psutil/_psutil_common.c @@ -68,15 +68,6 @@ PyErr_SetExcFromWindowsErrWithFilenameObject(PyObject *type, return PyErr_SetFromWindowsErrWithFilename(ierr, NULL); } #endif // !defined(PyErr_SetExcFromWindowsErrWithFilenameObject) - - -// PyPy 2.7 -#if !defined(PyErr_SetFromWindowsErr) -PyObject * -PyErr_SetFromWindowsErr(int winerr) { - return PyErr_SetFromWindowsErrWithFilename(winerr, ""); -} -#endif // !defined(PyErr_SetFromWindowsErr) #endif // defined(PSUTIL_WINDOWS) && defined(PYPY_VERSION) diff --git a/psutil/_psutil_common.h b/psutil/_psutil_common.h index 2cdfa9d4d..024452630 100644 --- a/psutil/_psutil_common.h +++ b/psutil/_psutil_common.h @@ -23,14 +23,6 @@ static const int PSUTIL_CONN_NONE = 128; // --- Backward compatibility with missing Python.h APIs // ==================================================================== -#if PY_MAJOR_VERSION < 3 - // On Python 2 we just return a plain byte string, which is never - // supposed to raise decoding errors, see: - // https://github.com/giampaolo/psutil/issues/1040 - #define PyUnicode_DecodeFSDefault PyString_FromString - #define PyUnicode_DecodeFSDefaultAndSize PyString_FromStringAndSize -#endif - #if defined(PSUTIL_WINDOWS) && defined(PYPY_VERSION) #if !defined(PyErr_SetFromWindowsErrWithFilename) PyObject *PyErr_SetFromWindowsErrWithFilename(int ierr, @@ -45,7 +37,6 @@ static const int PSUTIL_CONN_NONE = 128; // --- _Py_PARSE_PID // SIZEOF_INT|LONG is missing on Linux + PyPy (only?). -// SIZEOF_PID_T is missing on Windows + Python2. // In this case we guess it from setup.py. It's not 100% bullet proof, // If wrong we'll probably get compiler warnings. // FWIW on all UNIX platforms I've seen pid_t is defined as an int. @@ -60,8 +51,8 @@ static const int PSUTIL_CONN_NONE = 128; #define SIZEOF_PID_T PSUTIL_SIZEOF_PID_T // set as a macro in setup.py #endif -// _Py_PARSE_PID is Python 3 only, but since it's private make sure it's -// always present. +// _Py_PARSE_PID was added in Python 3, but since it's private we make +// sure it's always present. #ifndef _Py_PARSE_PID #if SIZEOF_PID_T == SIZEOF_INT #define _Py_PARSE_PID "i" @@ -75,14 +66,10 @@ static const int PSUTIL_CONN_NONE = 128; #endif #endif -// Python 2 or PyPy on Windows +// PyPy on Windows #ifndef PyLong_FromPid #if ((SIZEOF_PID_T == SIZEOF_INT) || (SIZEOF_PID_T == SIZEOF_LONG)) - #if PY_MAJOR_VERSION >= 3 - #define PyLong_FromPid PyLong_FromLong - #else - #define PyLong_FromPid PyInt_FromLong - #endif + #define PyLong_FromPid PyLong_FromLong #elif defined(SIZEOF_LONG_LONG) && SIZEOF_PID_T == SIZEOF_LONG_LONG #define PyLong_FromPid PyLong_FromLongLong #else diff --git a/psutil/_psutil_linux.c b/psutil/_psutil_linux.c index 46244c579..fcd886bfb 100644 --- a/psutil/_psutil_linux.c +++ b/psutil/_psutil_linux.c @@ -19,6 +19,13 @@ #include "arch/linux/proc.h" #include "arch/linux/users.h" +// May happen on old RedHat versions, see: +// https://github.com/giampaolo/psutil/issues/607 +#ifndef DUPLEX_UNKNOWN + #define DUPLEX_UNKNOWN 0xff +#endif + +#define INITERR return NULL static PyMethodDef mod_methods[] = { // --- per-process functions @@ -41,40 +48,24 @@ static PyMethodDef mod_methods[] = { {"set_debug", psutil_set_debug, METH_VARARGS}, {NULL, NULL, 0, NULL} }; -// May happen on old RedHat versions, see: -// https://github.com/giampaolo/psutil/issues/607 -#ifndef DUPLEX_UNKNOWN - #define DUPLEX_UNKNOWN 0xff -#endif - -#if PY_MAJOR_VERSION >= 3 - #define INITERR return NULL - static struct PyModuleDef moduledef = { - PyModuleDef_HEAD_INIT, - "_psutil_linux", - NULL, - -1, - mod_methods, - NULL, - NULL, - NULL, - NULL - }; +static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + "_psutil_linux", + NULL, + -1, + mod_methods, + NULL, + NULL, + NULL, + NULL +}; - PyObject *PyInit__psutil_linux(void) -#else /* PY_MAJOR_VERSION */ - #define INITERR return - void init_psutil_linux(void) -#endif /* PY_MAJOR_VERSION */ -{ -#if PY_MAJOR_VERSION >= 3 +PyObject * +PyInit__psutil_linux(void) { PyObject *mod = PyModule_Create(&moduledef); -#else - PyObject *mod = Py_InitModule("_psutil_linux", mod_methods); -#endif if (mod == NULL) INITERR; @@ -91,7 +82,5 @@ static PyMethodDef mod_methods[] = { if (mod == NULL) INITERR; -#if PY_MAJOR_VERSION >= 3 return mod; -#endif } diff --git a/psutil/_psutil_osx.c b/psutil/_psutil_osx.c index 09fa267a9..d9a486fe5 100644 --- a/psutil/_psutil_osx.c +++ b/psutil/_psutil_osx.c @@ -20,6 +20,8 @@ #include "arch/osx/sys.h" +#define INITERR return NULL + static PyMethodDef mod_methods[] = { // --- per-process functions {"proc_cmdline", psutil_proc_cmdline, METH_VARARGS}, @@ -61,33 +63,22 @@ static PyMethodDef mod_methods[] = { }; -#if PY_MAJOR_VERSION >= 3 - #define INITERR return NULL - - static struct PyModuleDef moduledef = { - PyModuleDef_HEAD_INIT, - "_psutil_osx", - NULL, - -1, - mod_methods, - NULL, - NULL, - NULL, - NULL - }; - - PyObject *PyInit__psutil_osx(void) -#else /* PY_MAJOR_VERSION */ - #define INITERR return - - void init_psutil_osx(void) -#endif /* PY_MAJOR_VERSION */ -{ -#if PY_MAJOR_VERSION >= 3 +static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + "_psutil_osx", + NULL, + -1, + mod_methods, + NULL, + NULL, + NULL, + NULL +}; + + +PyObject * +PyInit__psutil_osx(void) { PyObject *mod = PyModule_Create(&moduledef); -#else - PyObject *mod = Py_InitModule("_psutil_osx", mod_methods); -#endif if (mod == NULL) INITERR; @@ -140,7 +131,5 @@ static PyMethodDef mod_methods[] = { if (mod == NULL) INITERR; -#if PY_MAJOR_VERSION >= 3 return mod; -#endif } diff --git a/psutil/_psutil_posix.c b/psutil/_psutil_posix.c index 5df15530f..d4d16e7ff 100644 --- a/psutil/_psutil_posix.c +++ b/psutil/_psutil_posix.c @@ -52,6 +52,9 @@ #include "_psutil_common.h" +#define INITERR return NULL + + // ==================================================================== // --- Utils // ==================================================================== @@ -434,11 +437,7 @@ append_flag(PyObject *py_retlist, const char * flag_name) { PyObject *py_str = NULL; -#if PY_MAJOR_VERSION >= 3 py_str = PyUnicode_FromString(flag_name); -#else - py_str = PyString_FromString(flag_name); -#endif if (! py_str) return 0; if (PyList_Append(py_retlist, py_str)) { @@ -883,33 +882,21 @@ static PyMethodDef mod_methods[] = { }; -#if PY_MAJOR_VERSION >= 3 - #define INITERR return NULL - - static struct PyModuleDef moduledef = { - PyModuleDef_HEAD_INIT, - "_psutil_posix", - NULL, - -1, - mod_methods, - NULL, - NULL, - NULL, - NULL - }; - - PyObject *PyInit__psutil_posix(void) -#else /* PY_MAJOR_VERSION */ - #define INITERR return +static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + "_psutil_posix", + NULL, + -1, + mod_methods, + NULL, + NULL, + NULL, + NULL +}; - void init_psutil_posix(void) -#endif /* PY_MAJOR_VERSION */ -{ -#if PY_MAJOR_VERSION >= 3 +PyObject * +PyInit__psutil_posix(void) { PyObject *mod = PyModule_Create(&moduledef); -#else - PyObject *mod = Py_InitModule("_psutil_posix", mod_methods); -#endif if (mod == NULL) INITERR; @@ -1022,9 +1009,7 @@ static PyMethodDef mod_methods[] = { if (mod == NULL) INITERR; -#if PY_MAJOR_VERSION >= 3 return mod; -#endif } #ifdef __cplusplus diff --git a/psutil/_psutil_sunos.c b/psutil/_psutil_sunos.c index d21f59c61..dde9f7015 100644 --- a/psutil/_psutil_sunos.c +++ b/psutil/_psutil_sunos.c @@ -59,6 +59,8 @@ #include "arch/solaris/environ.h" #define PSUTIL_TV2DOUBLE(t) (((t).tv_nsec * 0.000000001) + (t).tv_sec) +#define GETSTATE(m) ((struct module_state*)PyModule_GetState(m)) +#define INITERROR return NULL /* @@ -1671,13 +1673,6 @@ struct module_state { PyObject *error; }; -#if PY_MAJOR_VERSION >= 3 -#define GETSTATE(m) ((struct module_state*)PyModule_GetState(m)) -#else -#define GETSTATE(m) (&_state) -#endif - -#if PY_MAJOR_VERSION >= 3 static int psutil_sunos_traverse(PyObject *m, visitproc visit, void *arg) { @@ -1703,21 +1698,10 @@ static struct PyModuleDef moduledef = { NULL }; -#define INITERROR return NULL - -PyMODINIT_FUNC PyInit__psutil_sunos(void) -#else -#define INITERROR return - -void init_psutil_sunos(void) -#endif -{ -#if PY_MAJOR_VERSION >= 3 +PyMODINIT_FUNC +PyInit__psutil_sunos(void) { PyObject *module = PyModule_Create(&moduledef); -#else - PyObject *module = Py_InitModule("_psutil_sunos", PsutilMethods); -#endif if (module == NULL) INITERROR; @@ -1766,7 +1750,6 @@ void init_psutil_sunos(void) if (module == NULL) INITERROR; -#if PY_MAJOR_VERSION >= 3 + return module; -#endif } diff --git a/psutil/_psutil_windows.c b/psutil/_psutil_windows.c index 0c221bdc2..0af18f3e2 100644 --- a/psutil/_psutil_windows.c +++ b/psutil/_psutil_windows.c @@ -34,6 +34,10 @@ #include "arch/windows/wmi.h" +#define INITERROR return NULL +#define GETSTATE(m) ((struct module_state*)PyModule_GetState(m)) + + // ------------------------ Python init --------------------------- static PyMethodDef @@ -116,21 +120,15 @@ struct module_state { PyObject *error; }; -#if PY_MAJOR_VERSION >= 3 -#define GETSTATE(m) ((struct module_state*)PyModule_GetState(m)) -#else -#define GETSTATE(m) (&_state) -static struct module_state _state; -#endif - -#if PY_MAJOR_VERSION >= 3 -static int psutil_windows_traverse(PyObject *m, visitproc visit, void *arg) { +static int +psutil_windows_traverse(PyObject *m, visitproc visit, void *arg) { Py_VISIT(GETSTATE(m)->error); return 0; } -static int psutil_windows_clear(PyObject *m) { +static int +psutil_windows_clear(PyObject *m) { Py_CLEAR(GETSTATE(m)->error); return 0; } @@ -147,21 +145,12 @@ static struct PyModuleDef moduledef = { NULL }; -#define INITERROR return NULL - -PyMODINIT_FUNC PyInit__psutil_windows(void) -#else -#define INITERROR return -void init_psutil_windows(void) -#endif -{ +PyMODINIT_FUNC +PyInit__psutil_windows(void) { struct module_state *st = NULL; -#if PY_MAJOR_VERSION >= 3 + PyObject *module = PyModule_Create(&moduledef); -#else - PyObject *module = Py_InitModule("_psutil_windows", PsutilMethods); -#endif if (module == NULL) INITERROR; @@ -283,7 +272,5 @@ void init_psutil_windows(void) PyModule_AddIntConstant( module, "WINDOWS_10", PSUTIL_WINDOWS_10); -#if PY_MAJOR_VERSION >= 3 return module; -#endif } diff --git a/psutil/_pswindows.py b/psutil/_pswindows.py index e39ba711f..be100493e 100644 --- a/psutil/_pswindows.py +++ b/psutil/_pswindows.py @@ -5,7 +5,7 @@ """Windows platform implementation.""" import contextlib -import errno +import enum import functools import os import signal @@ -15,7 +15,6 @@ from . import _common from ._common import ENCODING -from ._common import ENCODING_ERRS from ._common import AccessDenied from ._common import NoSuchProcess from ._common import TimeoutExpired @@ -27,11 +26,6 @@ from ._common import memoize_when_activated from ._common import parse_environ_block from ._common import usage_percent -from ._compat import PY3 -from ._compat import long -from ._compat import lru_cache -from ._compat import range -from ._compat import unicode from ._psutil_windows import ABOVE_NORMAL_PRIORITY_CLASS from ._psutil_windows import BELOW_NORMAL_PRIORITY_CLASS from ._psutil_windows import HIGH_PRIORITY_CLASS @@ -58,10 +52,6 @@ else: raise -if PY3: - import enum -else: - enum = None # process priority constants, import from __init__.py: # http://msdn.microsoft.com/en-us/library/ms686219(v=vs.85).aspx @@ -88,11 +78,8 @@ ERROR_PARTIAL_COPY = 299 PYPY = '__pypy__' in sys.builtin_module_names -if enum is None: - AF_LINK = -1 -else: - AddressFamily = enum.IntEnum('AddressFamily', {'AF_LINK': -1}) - AF_LINK = AddressFamily.AF_LINK +AddressFamily = enum.IntEnum('AddressFamily', {'AF_LINK': -1}) +AF_LINK = AddressFamily.AF_LINK TCP_STATUSES = { cext.MIB_TCP_STATE_ESTAB: _common.CONN_ESTABLISHED, @@ -110,32 +97,27 @@ cext.PSUTIL_CONN_NONE: _common.CONN_NONE, } -if enum is not 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 +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__) -if enum is None: +globals().update(Priority.__members__) + + +class IOPriority(enum.IntEnum): IOPRIO_VERYLOW = 0 IOPRIO_LOW = 1 IOPRIO_NORMAL = 2 IOPRIO_HIGH = 3 -else: - class IOPriority(enum.IntEnum): - IOPRIO_VERYLOW = 0 - IOPRIO_LOW = 1 - IOPRIO_NORMAL = 2 - IOPRIO_HIGH = 3 - globals().update(IOPriority.__members__) +globals().update(IOPriority.__members__) pinfo_map = dict( num_handles=0, @@ -199,7 +181,7 @@ class IOPriority(enum.IntEnum): # ===================================================================== -@lru_cache(maxsize=512) +@functools.lru_cache(maxsize=512) def convert_dos_path(s): r"""Convert paths using native DOS format like: "\Device\HarddiskVolume1\Windows\systemew\file.txt" @@ -212,18 +194,6 @@ def convert_dos_path(s): return os.path.join(driveletter, remainder) -def py2_strencode(s): - """Encode a unicode string to a byte string by using the default fs - encoding + "replace" error handler. - """ - if PY3: - return s - if isinstance(s, str): - return s - else: - return s.encode(ENCODING, ENCODING_ERRS) - - @memoize def getpagesize(): return cext.getpagesize() @@ -282,7 +252,7 @@ def swap_memory(): def disk_usage(path): """Return disk usage associated with path.""" - if PY3 and isinstance(path, bytes): + 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") @@ -408,9 +378,6 @@ def net_if_stats(): ret = {} rawdict = cext.net_if_stats() for name, items in rawdict.items(): - if not PY3: - assert isinstance(name, unicode), type(name) - name = py2_strencode(name) isup, duplex, speed, mtu = items if hasattr(_common, 'NicDuplex'): duplex = _common.NicDuplex(duplex) @@ -422,18 +389,12 @@ def net_io_counters(): """Return network I/O statistics for every network interface installed on the system as a dict of raw tuples. """ - ret = cext.net_io_counters() - return dict([(py2_strencode(k), v) for k, v in ret.items()]) + return cext.net_io_counters() def net_if_addrs(): """Return the addresses associated to each NIC.""" - ret = [] - for items in cext.net_if_addrs(): - items = list(items) - items[0] = py2_strencode(items[0]) - ret.append(items) - return ret + return cext.net_if_addrs() # ===================================================================== @@ -489,7 +450,6 @@ def users(): rawlist = cext.users() for item in rawlist: user, hostname, tstamp = item - user = py2_strencode(user) nt = _common.suser(user, None, hostname, tstamp, None) retlist.append(nt) return retlist @@ -503,7 +463,7 @@ def users(): def win_service_iter(): """Yields a list of WindowsService instances.""" for name, display_name in cext.winservice_enumerate(): - yield WindowsService(py2_strencode(name), py2_strencode(display_name)) + yield WindowsService(name, display_name) def win_service_get(name): @@ -547,10 +507,10 @@ def _query_config(self): ) # XXX - update _self.display_name? return dict( - display_name=py2_strencode(display_name), - binpath=py2_strencode(binpath), - username=py2_strencode(username), - start_type=py2_strencode(start_type), + display_name=display_name, + binpath=binpath, + username=username, + start_type=start_type, ) def _query_status(self): @@ -628,7 +588,7 @@ def status(self): def description(self): """Service long description.""" - return py2_strencode(cext.winservice_query_descr(self.name())) + return cext.winservice_query_descr(self.name()) # utils @@ -696,12 +656,7 @@ def as_dict(self): def is_permission_err(exc): """Return True if this is a permission error.""" assert isinstance(exc, OSError), exc - if exc.errno in {errno.EPERM, errno.EACCES}: - return True - # On Python 2 OSError doesn't always have 'winerror'. Sometimes - # it does, in which case the original exception was WindowsError - # (which is a subclass of OSError). - return getattr(exc, "winerror", -1) in { + return isinstance(exc, PermissionError) or exc.winerror in { cext.ERROR_ACCESS_DENIED, cext.ERROR_PRIVILEGE_NOT_HELD, } @@ -712,7 +667,7 @@ def convert_oserror(exc, pid=None, name=None): assert isinstance(exc, OSError), exc if is_permission_err(exc): return AccessDenied(pid=pid, name=name) - if exc.errno == errno.ESRCH: + if isinstance(exc, ProcessLookupError): return NoSuchProcess(pid=pid, name=name) raise exc @@ -742,7 +697,7 @@ def wrapper(self, *args, **kwargs): for _ in range(times): # retries for roughly 1 second try: return fun(self, *args, **kwargs) - except WindowsError as _: + except OSError as _: err = _ if err.winerror == ERROR_PARTIAL_COPY: time.sleep(delay) @@ -750,8 +705,8 @@ def wrapper(self, *args, **kwargs): continue raise msg = ( - "{} retried {} times, converted to AccessDenied as it's still" - "returning {}".format(fun, times, err) + 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) @@ -805,7 +760,7 @@ def exe(self): if PYPY: try: exe = cext.proc_exe(self.pid) - except WindowsError as err: + 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: @@ -814,8 +769,6 @@ def exe(self): raise else: exe = cext.proc_exe(self.pid) - if not PY3: - exe = py2_strencode(exe) if exe.startswith('\\'): return convert_dos_path(exe) return exe # May be "Registry", "MemCompression", ... @@ -827,26 +780,20 @@ def cmdline(self): # PEB method detects cmdline changes but requires more # privileges: https://github.com/giampaolo/psutil/pull/1398 try: - ret = cext.proc_cmdline(self.pid, use_peb=True) + return cext.proc_cmdline(self.pid, use_peb=True) except OSError as err: if is_permission_err(err): - ret = cext.proc_cmdline(self.pid, use_peb=False) + return cext.proc_cmdline(self.pid, use_peb=False) else: raise else: - ret = cext.proc_cmdline(self.pid, use_peb=True) - if PY3: - return ret - else: - return [py2_strencode(s) for s in ret] + return cext.proc_cmdline(self.pid, use_peb=True) @wrap_exceptions @retry_error_partial_copy def environ(self): - ustr = cext.proc_environ(self.pid) - if ustr and not PY3: - assert isinstance(ustr, unicode), type(ustr) - return parse_environ_block(py2_strencode(ustr)) + s = cext.proc_environ(self.pid) + return parse_environ_block(s) def ppid(self): try: @@ -904,8 +851,6 @@ def memory_maps(self): else: for addr, perm, path, rss in raw: path = convert_dos_path(path) - if not PY3: - path = py2_strencode(path) addr = hex(addr) yield (addr, perm, path, rss) @@ -917,11 +862,7 @@ def kill(self): def send_signal(self, sig): if sig == signal.SIGTERM: cext.proc_kill(self.pid) - # py >= 2.7 - elif sig in { - getattr(signal, "CTRL_C_EVENT", object()), - getattr(signal, "CTRL_BREAK_EVENT", object()), - }: + elif sig in {signal.CTRL_C_EVENT, signal.CTRL_BREAK_EVENT}: os.kill(self.pid, sig) else: msg = ( @@ -978,7 +919,7 @@ def username(self): if self.pid in {0, 4}: return 'NT AUTHORITY\\SYSTEM' domain, user = cext.proc_username(self.pid) - return py2_strencode(domain) + '\\' + py2_strencode(user) + return f"{domain}\\{user}" @wrap_exceptions def create_time(self, fast_only=False): @@ -1038,7 +979,7 @@ def cwd(self): # return a normalized pathname since the native C function appends # "\\" at the and of the path path = cext.proc_cwd(self.pid) - return py2_strencode(os.path.normpath(path)) + return os.path.normpath(path) @wrap_exceptions def open_files(self): @@ -1053,8 +994,6 @@ def open_files(self): for _file in raw_file_names: _file = convert_dos_path(_file) if isfile_strict(_file): - if not PY3: - _file = py2_strencode(_file) ntuple = _common.popenfile(_file, -1) ret.add(ntuple) return list(ret) @@ -1066,8 +1005,7 @@ def net_connections(self, kind='inet'): @wrap_exceptions def nice_get(self): value = cext.proc_priority_get(self.pid) - if enum is not None: - value = Priority(value) + value = Priority(value) return value @wrap_exceptions @@ -1077,8 +1015,7 @@ def nice_set(self, value): @wrap_exceptions def ionice_get(self): ret = cext.proc_io_priority_get(self.pid) - if enum is not None: - ret = IOPriority(ret) + ret = IOPriority(ret) return ret @wrap_exceptions @@ -1087,10 +1024,10 @@ def ionice_set(self, ioclass, value): msg = "value argument not accepted on Windows" raise TypeError(msg) if ioclass not in { - IOPRIO_VERYLOW, - IOPRIO_LOW, - IOPRIO_NORMAL, - IOPRIO_HIGH, + IOPriority.IOPRIO_VERYLOW, + IOPriority.IOPRIO_LOW, + IOPriority.IOPRIO_NORMAL, + IOPriority.IOPRIO_HIGH, }: raise ValueError("%s is not a valid priority" % ioclass) cext.proc_io_priority_set(self.pid, ioclass) @@ -1146,7 +1083,7 @@ def to_bitmask(ls): allcpus = list(range(len(per_cpu_times()))) for cpu in value: if cpu not in allcpus: - if not isinstance(cpu, (int, long)): + if not isinstance(cpu, int): raise TypeError( "invalid CPU %r; an integer is required" % cpu ) diff --git a/psutil/arch/freebsd/proc.c b/psutil/arch/freebsd/proc.c index a81128b51..5c6fab971 100644 --- a/psutil/arch/freebsd/proc.c +++ b/psutil/arch/freebsd/proc.c @@ -611,11 +611,7 @@ psutil_proc_cpu_affinity_set(PyObject *self, PyObject *args) { CPU_ZERO(&cpu_set); for (i = 0; i < seq_len; i++) { PyObject *item = PySequence_Fast_GET_ITEM(py_cpu_seq, i); -#if PY_MAJOR_VERSION >= 3 long value = PyLong_AsLong(item); -#else - long value = PyInt_AsLong(item); -#endif if (value == -1 || PyErr_Occurred()) goto error; CPU_SET(value, &cpu_set); diff --git a/psutil/arch/linux/proc.c b/psutil/arch/linux/proc.c index b58a3ce2a..f8230ee75 100644 --- a/psutil/arch/linux/proc.c +++ b/psutil/arch/linux/proc.c @@ -118,11 +118,7 @@ psutil_proc_cpu_affinity_get(PyObject *self, PyObject *args) { cpucount_s = CPU_COUNT_S(setsize, mask); for (cpu = 0, count = cpucount_s; count; cpu++) { if (CPU_ISSET_S(cpu, setsize, mask)) { -#if PY_MAJOR_VERSION >= 3 PyObject *cpu_num = PyLong_FromLong(cpu); -#else - PyObject *cpu_num = PyInt_FromLong(cpu); -#endif if (cpu_num == NULL) goto error; if (PyList_Append(py_list, cpu_num)) { @@ -159,11 +155,7 @@ psutil_proc_cpu_affinity_set(PyObject *self, PyObject *args) { if (!PySequence_Check(py_cpu_set)) { return PyErr_Format( PyExc_TypeError, -#if PY_MAJOR_VERSION >= 3 "sequence argument expected, got %R", Py_TYPE(py_cpu_set) -#else - "sequence argument expected, got %s", Py_TYPE(py_cpu_set)->tp_name -#endif ); } @@ -177,11 +169,7 @@ psutil_proc_cpu_affinity_set(PyObject *self, PyObject *args) { if (!item) { return NULL; } -#if PY_MAJOR_VERSION >= 3 long value = PyLong_AsLong(item); -#else - long value = PyInt_AsLong(item); -#endif Py_XDECREF(item); if ((value == -1) || PyErr_Occurred()) { if (!PyErr_Occurred()) diff --git a/psutil/arch/osx/disk.c b/psutil/arch/osx/disk.c index d02cf794d..e1a8f5a49 100644 --- a/psutil/arch/osx/disk.c +++ b/psutil/arch/osx/disk.c @@ -168,7 +168,6 @@ psutil_disk_usage_used(PyObject *self, PyObject *args) { PyObject *py_mount_point_bytes = NULL; char* mount_point; -#if PY_MAJOR_VERSION >= 3 if (!PyArg_ParseTuple(args, "O&O", PyUnicode_FSConverter, &py_mount_point_bytes, &py_default_value)) { return NULL; } @@ -177,11 +176,6 @@ psutil_disk_usage_used(PyObject *self, PyObject *args) { Py_XDECREF(py_mount_point_bytes); return NULL; } -#else - if (!PyArg_ParseTuple(args, "sO", &mount_point, &py_default_value)) { - return NULL; - } -#endif #ifdef ATTR_VOL_SPACEUSED /* Call getattrlist(ATTR_VOL_SPACEUSED) to get used space info. */ diff --git a/psutil/arch/windows/disk.c b/psutil/arch/windows/disk.c index 004174561..552929f38 100644 --- a/psutil/arch/windows/disk.c +++ b/psutil/arch/windows/disk.c @@ -44,33 +44,6 @@ PyObject * psutil_disk_usage(PyObject *self, PyObject *args) { BOOL retval; ULARGE_INTEGER _, total, free; - -#if PY_MAJOR_VERSION <= 2 - char *path; - - if (PyArg_ParseTuple(args, "u", &path)) { - Py_BEGIN_ALLOW_THREADS - retval = GetDiskFreeSpaceExW((LPCWSTR)path, &_, &total, &free); - Py_END_ALLOW_THREADS - goto return_; - } - - // on Python 2 we also want to accept plain strings other - // than Unicode - PyErr_Clear(); // drop the argument parsing error - if (PyArg_ParseTuple(args, "s", &path)) { - Py_BEGIN_ALLOW_THREADS - retval = GetDiskFreeSpaceEx(path, &_, &total, &free); - Py_END_ALLOW_THREADS - goto return_; - } - - return NULL; - -return_: - if (retval == 0) - return PyErr_SetFromWindowsErrWithFilename(0, path); -#else PyObject *py_path; wchar_t *path; @@ -91,7 +64,7 @@ psutil_disk_usage(PyObject *self, PyObject *args) { if (retval == 0) return PyErr_SetExcFromWindowsErrWithFilenameObject(PyExc_OSError, 0, py_path); -#endif + return Py_BuildValue("(LL)", total.QuadPart, free.QuadPart); } diff --git a/psutil/arch/windows/net.c b/psutil/arch/windows/net.c index 8d8f7d1c0..9a5634b06 100644 --- a/psutil/arch/windows/net.c +++ b/psutil/arch/windows/net.c @@ -255,21 +255,14 @@ psutil_net_if_addrs(PyObject *self, PyObject *args) { continue; } -#if PY_MAJOR_VERSION >= 3 py_address = PyUnicode_FromString(buff_addr); -#else - py_address = PyString_FromString(buff_addr); -#endif if (py_address == NULL) goto error; if (netmaskIntRet != NULL) { -#if PY_MAJOR_VERSION >= 3 py_netmask = PyUnicode_FromString(buff_netmask); -#else - py_netmask = PyString_FromString(buff_netmask); -#endif - } else { + } + else { Py_INCREF(Py_None); py_netmask = Py_None; } diff --git a/psutil/arch/windows/proc.c b/psutil/arch/windows/proc.c index 05fb50255..41fa9dda6 100644 --- a/psutil/arch/windows/proc.c +++ b/psutil/arch/windows/proc.c @@ -192,11 +192,7 @@ psutil_proc_wait(PyObject *self, PyObject *args) { CloseHandle(hProcess); -#if PY_MAJOR_VERSION >= 3 return PyLong_FromLong((long) ExitCode); -#else - return PyInt_FromLong((long) ExitCode); -#endif } diff --git a/psutil/tests/__init__.py b/psutil/tests/__init__.py index f2cceeac5..13fed1778 100644 --- a/psutil/tests/__init__.py +++ b/psutil/tests/__init__.py @@ -1,19 +1,19 @@ -# -*- coding: utf-8 -*- - # 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.""" -from __future__ import print_function import atexit import contextlib import ctypes +import enum import errno import functools import gc +import importlib +import ipaddress import os import platform import random @@ -56,29 +56,8 @@ from psutil._common import memoize from psutil._common import print_color from psutil._common import supports_ipv6 -from psutil._compat import PY3 -from psutil._compat import FileExistsError -from psutil._compat import FileNotFoundError -from psutil._compat import range -from psutil._compat import super -from psutil._compat import unicode -from psutil._compat import which -try: - from unittest import mock # py3 -except ImportError: - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - import mock # NOQA - requires "pip install mock" - -if PY3: - import enum -else: - import unittest2 as unittest - - enum = None - if POSIX: from psutil._psposix import wait_pid @@ -86,7 +65,7 @@ # fmt: off __all__ = [ # constants - 'APPVEYOR', 'DEVNULL', 'GLOBAL_TIMEOUT', 'TOLERANCE_SYS_MEM', 'NO_RETRIES', + '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', @@ -131,9 +110,8 @@ PYPY = '__pypy__' in sys.builtin_module_names # whether we're running this test suite on a Continuous Integration service -APPVEYOR = 'APPVEYOR' in os.environ GITHUB_ACTIONS = 'GITHUB_ACTIONS' in os.environ or 'CIBUILDWHEEL' in os.environ -CI_TESTING = APPVEYOR or GITHUB_ACTIONS +CI_TESTING = GITHUB_ACTIONS COVERAGE = 'COVERAGE_RUN' in os.environ PYTEST_PARALLEL = "PYTEST_XDIST_WORKER" in os.environ # `make test-parallel` if LINUX and GITHUB_ACTIONS: @@ -199,13 +177,10 @@ def macos_version(): TESTFN_PREFIX = '$psutil-%s-' % os.getpid() else: TESTFN_PREFIX = '@psutil-%s-' % os.getpid() -UNICODE_SUFFIX = u"-ƒőő" +UNICODE_SUFFIX = "-ƒőő" # An invalid unicode string. -if PY3: - INVALID_UNICODE_SUFFIX = b"f\xc0\x80".decode('utf8', 'surrogateescape') -else: - INVALID_UNICODE_SUFFIX = "f\xc0\x80" -ASCII_FS = sys.getfilesystemencoding().lower() in {'ascii', 'us-ascii'} +INVALID_UNICODE_SUFFIX = b"f\xc0\x80".decode('utf8', 'surrogateescape') +ASCII_FS = sys.getfilesystemencoding().lower() in {"ascii", "us-ascii"} # --- paths @@ -273,7 +248,7 @@ def attempt(exe): exe = ( attempt(sys.executable) or attempt(os.path.realpath(sys.executable)) - or attempt(which("python%s.%s" % sys.version_info[:2])) + or attempt(shutil.which("python%s.%s" % sys.version_info[:2])) or attempt(psutil.Process().exe()) ) if not exe: @@ -458,13 +433,9 @@ def spawn_zombie(): time.sleep(3000) else: # this is the zombie process - s = socket.socket(socket.AF_UNIX) - with contextlib.closing(s): + with socket.socket(socket.AF_UNIX) as s: s.connect('%s') - if sys.version_info < (3, ): - pid = str(os.getpid()) - else: - pid = bytes(str(os.getpid()), 'ascii') + pid = bytes(str(os.getpid()), 'ascii') s.sendall(pid) """ % unix_file) tfile = None @@ -524,10 +495,7 @@ def sh(cmd, **kwds): cmd = shlex.split(cmd) p = subprocess.Popen(cmd, **kwds) _subprocesses_started.add(p) - if PY3: - stdout, stderr = p.communicate(timeout=GLOBAL_TIMEOUT) - else: - stdout, stderr = p.communicate() + stdout, stderr = p.communicate(timeout=GLOBAL_TIMEOUT) if p.returncode != 0: raise RuntimeError(stdout + stderr) if stderr: @@ -549,10 +517,7 @@ def terminate(proc_or_pid, sig=signal.SIGTERM, wait_timeout=GLOBAL_TIMEOUT): """ def wait(proc, timeout): - if isinstance(proc, subprocess.Popen) and not PY3: - proc.wait() - else: - proc.wait(timeout) + proc.wait(timeout) if WINDOWS and isinstance(proc, subprocess.Popen): # Otherwise PID may still hang around. try: @@ -573,11 +538,12 @@ def sendsig(proc, 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 - elif err.errno != errno.ESRCH: - raise + raise return wait(proc, timeout) def term_psutil_proc(proc, timeout): @@ -688,11 +654,7 @@ def get_winver(): if not WINDOWS: raise NotImplementedError("not WINDOWS") wv = sys.getwindowsversion() - if hasattr(wv, 'service_pack_major'): # python >= 2.7 - sp = wv.service_pack_major or 0 - else: - r = re.search(r"\s\d$", wv[4]) - sp = int(r.group(0)) if r else 0 + sp = wv.service_pack_major or 0 return (wv[0], wv[1], sp) @@ -749,10 +711,8 @@ def wrapper(*args, **kwargs): self.logfun(exc) self.sleep() continue - if PY3: - raise exc # noqa: PLE0704 - else: - raise # noqa: PLE0704 + + raise exc # noqa: PLE0704 # This way the user of the decorated function can change config # parameters. @@ -824,7 +784,7 @@ def retry_fun(fun): return fun() except FileNotFoundError: pass - except WindowsError as _: + except OSError as _: err = _ warn("ignoring %s" % (str(err))) time.sleep(0.01) @@ -877,7 +837,7 @@ def create_py_exe(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 which("gcc"): + if not shutil.which("gcc"): raise pytest.skip("gcc is not installed") if c_code is None: c_code = textwrap.dedent(""" @@ -959,7 +919,7 @@ def context(exc, match=None): yield einfo except exc as err: if match and not re.search(match, str(err)): - msg = '"{}" does not match "{}"'.format(match, str(err)) + msg = f'"{match}" does not match "{str(err)}"' raise AssertionError(msg) einfo._exc = err else: @@ -1000,24 +960,7 @@ def __call__(self, cls_or_meth): pytest = fake_pytest -class TestCase(unittest.TestCase): - # ...otherwise multiprocessing.Pool complains - if not PY3: - - def runTest(self): - pass - - @contextlib.contextmanager - def subTest(self, *args, **kw): - # fake it for python 2.7 - yield - - -# monkey patch default unittest.TestCase -unittest.TestCase = TestCase - - -class PsutilTestCase(TestCase): +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. @@ -1345,7 +1288,7 @@ def print_sysinfo(): info = collections.OrderedDict() # OS - if psutil.LINUX and which('lsb_release'): + if psutil.LINUX and shutil.which("lsb_release"): info['OS'] = sh('lsb_release -d -s') elif psutil.OSX: info['OS'] = 'Darwin %s' % platform.mac_ver()[0] @@ -1373,7 +1316,7 @@ def print_sysinfo(): # UNIX if psutil.POSIX: - if which('gcc'): + if shutil.which("gcc"): out = sh(['gcc', '--version']) info['gcc'] = str(out).split('\n')[0] else: @@ -1427,7 +1370,7 @@ def print_sysinfo(): # if WINDOWS: # os.system("tasklist") - # elif which("ps"): + # elif shutil.which("ps"): # os.system("ps aux") # print("=" * 70, file=sys.stderr) # NOQA @@ -1591,9 +1534,9 @@ def test_class_coverage(cls, test_class, ls): @classmethod def test(cls): - this = set([x[0] for x in cls.all]) - ignored = set([x[0] for x in cls.ignored]) - klass = set([x for x in dir(psutil.Process) if x[0] != '_']) + 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("uncovered Process class names: %r" % leftout) @@ -1732,7 +1675,7 @@ def wrapper(*args, **kwargs): # XXX: no longer used def get_free_port(host='127.0.0.1'): """Return an unused TCP port. Subject to race conditions.""" - with contextlib.closing(socket.socket()) as sock: + with socket.socket() as sock: sock.bind((host, 0)) return sock.getsockname()[1] @@ -1773,7 +1716,7 @@ def tcp_socketpair(family, addr=("", 0)): """Build a pair of TCP sockets connected to each other. Return a (server, client) tuple. """ - with contextlib.closing(socket.socket(family, SOCK_STREAM)) as ll: + with socket.socket(family, SOCK_STREAM) as ll: ll.bind(addr) ll.listen(5) addr = ll.getsockname() @@ -1846,22 +1789,15 @@ def check_net_address(addr, family): """Check a net address validity. Supported families are IPv4, IPv6 and MAC addresses. """ - import ipaddress # python >= 3.3 / requires "pip install ipaddress" - - if enum and PY3 and not PYPY: - assert isinstance(family, enum.IntEnum), family + 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 - if not PY3: - addr = unicode(addr) ipaddress.IPv4Address(addr) elif family == socket.AF_INET6: assert isinstance(addr, str), addr - if not PY3: - addr = unicode(addr) ipaddress.IPv6Address(addr) elif family == psutil.AF_LINK: assert re.match(r'([a-fA-F0-9]{2}[:|\-]?){6}', addr) is not None, addr @@ -1886,20 +1822,16 @@ def check_ntuple(conn): def check_family(conn): assert conn.family in {AF_INET, AF_INET6, AF_UNIX}, conn.family - if enum is not None: - assert isinstance(conn.family, enum.IntEnum), conn - else: - assert isinstance(conn.family, int), conn + 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() - s = socket.socket(conn.family, conn.type) - with contextlib.closing(s): + with socket.socket(conn.family, conn.type) as s: try: s.bind((conn.laddr[0], 0)) - except socket.error as err: + except OSError as err: if err.errno != errno.EADDRNOTAVAIL: raise elif conn.family == AF_UNIX: @@ -1913,10 +1845,7 @@ def check_type(conn): socket.SOCK_DGRAM, SOCK_SEQPACKET, }, conn.type - if enum is not None: - assert isinstance(conn.type, enum.IntEnum), conn - else: - assert isinstance(conn.type, int), conn + assert isinstance(conn.type, enum.IntEnum), conn if conn.type == socket.SOCK_DGRAM: assert conn.status == psutil.CONN_NONE, conn.status @@ -1966,38 +1895,20 @@ def filter_proc_net_connections(cons): # =================================================================== -# --- compatibility +# --- import utils # =================================================================== def reload_module(module): - """Backport of importlib.reload of Python 3.3+.""" - try: - import importlib - - if not hasattr(importlib, 'reload'): # python <=3.3 - raise ImportError - except ImportError: - import imp - - return imp.reload(module) - else: - return importlib.reload(module) + return importlib.reload(module) def import_module_by_path(path): name = os.path.splitext(os.path.basename(path))[0] - if sys.version_info[0] < 3: - import imp - - return imp.load_source(name, path) - else: - import importlib.util - - spec = importlib.util.spec_from_file_location(name, path) - mod = importlib.util.module_from_spec(spec) - spec.loader.exec_module(mod) - return mod + spec = importlib.util.spec_from_file_location(name, path) + mod = importlib.util.module_from_spec(spec) + spec.loader.exec_module(mod) + return mod # =================================================================== diff --git a/psutil/tests/test_bsd.py b/psutil/tests/test_bsd.py index 2fd1015d7..8e53f4f3e 100755 --- a/psutil/tests/test_bsd.py +++ b/psutil/tests/test_bsd.py @@ -9,10 +9,10 @@ """Tests specific to all BSD platforms.""" - import datetime import os import re +import shutil import time import psutil @@ -28,7 +28,6 @@ from psutil.tests import sh from psutil.tests import spawn_testproc from psutil.tests import terminate -from psutil.tests import which if BSD: @@ -36,7 +35,7 @@ PAGESIZE = getpagesize() # muse requires root privileges - MUSE_AVAILABLE = os.getuid() == 0 and which('muse') + MUSE_AVAILABLE = os.getuid() == 0 and shutil.which("muse") else: PAGESIZE = None MUSE_AVAILABLE = False @@ -122,12 +121,16 @@ def df(path): if abs(usage.used - used) > 10 * 1024 * 1024: raise self.fail("psutil=%s, df=%s" % (usage.used, used)) - @pytest.mark.skipif(not which('sysctl'), reason="sysctl cmd not available") + @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 which('sysctl'), reason="sysctl cmd not available") + @pytest.mark.skipif( + not shutil.which("sysctl"), reason="sysctl cmd not available" + ) @pytest.mark.skipif( NETBSD, reason="skipped on NETBSD" # we check /proc/meminfo ) @@ -136,7 +139,7 @@ def test_virtual_memory_total(self): assert num == psutil.virtual_memory().total @pytest.mark.skipif( - not which('ifconfig'), reason="ifconfig cmd not available" + not shutil.which("ifconfig"), reason="ifconfig cmd not available" ) def test_net_if_stats(self): for name, stats in psutil.net_if_stats().items(): @@ -423,9 +426,7 @@ def secs2hours(secs): return "%d:%02d" % (h, m) out = sh("acpiconf -i 0") - fields = dict( - [(x.split('\t')[0], x.split('\t')[-1]) for x in out.split("\n")] - ) + 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:'] diff --git a/psutil/tests/test_connections.py b/psutil/tests/test_connections.py index bca12ff4b..47f69fed5 100755 --- a/psutil/tests/test_connections.py +++ b/psutil/tests/test_connections.py @@ -25,7 +25,6 @@ from psutil import SUNOS from psutil import WINDOWS from psutil._common import supports_ipv6 -from psutil._compat import PY3 from psutil.tests import AF_UNIX from psutil.tests import HAS_NET_CONNECTIONS_UNIX from psutil.tests import SKIP_SYSCONS @@ -109,7 +108,7 @@ class TestUnconnectedSockets(ConnectionTestCase): def get_conn_from_sock(self, sock): cons = this_proc_net_connections(kind='all') - smap = dict([(c.fd, c) for c in cons]) + 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. @@ -137,7 +136,7 @@ def check_socket(self, sock): # local address laddr = sock.getsockname() - if not laddr and PY3 and isinstance(laddr, bytes): + if not laddr and isinstance(laddr, bytes): # See: http://bugs.python.org/issue30205 laddr = laddr.decode() if sock.family == AF_INET6: diff --git a/psutil/tests/test_contracts.py b/psutil/tests/test_contracts.py index c0ec6a8f7..7406d98ad 100755 --- a/psutil/tests/test_contracts.py +++ b/psutil/tests/test_contracts.py @@ -22,13 +22,11 @@ from psutil import POSIX from psutil import SUNOS from psutil import WINDOWS -from psutil._compat import long 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 PYPY from psutil.tests import QEMU_USER from psutil.tests import SKIP_SYSCONS from psutil.tests import PsutilTestCase @@ -198,7 +196,6 @@ def test_memory_maps(self): class TestSystemAPITypes(PsutilTestCase): """Check the return types of system related APIs. - Mainly we want to test we never return unicode on Python 2, see: https://github.com/giampaolo/psutil/issues/1039. """ @@ -237,13 +234,13 @@ def test_cpu_count(self): 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, long)) + 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, long)) + self.assert_ntuple_of_nums(v, type_=int) def test_disk_partitions(self): # Duplicate of test_system.py. Keep it anyway. @@ -266,10 +263,7 @@ def test_net_if_addrs(self): for ifname, addrs in psutil.net_if_addrs().items(): assert isinstance(ifname, str) for addr in addrs: - if enum is not None and not PYPY: - assert isinstance(addr.family, enum.IntEnum) - else: - assert isinstance(addr.family, int) + 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))) @@ -280,10 +274,7 @@ def test_net_if_stats(self): for ifname, info in psutil.net_if_stats().items(): assert isinstance(ifname, str) assert isinstance(info.isup, bool) - if enum is not None: - assert isinstance(info.duplex, enum.IntEnum) - else: - assert isinstance(info.duplex, int) + assert isinstance(info.duplex, enum.IntEnum) assert isinstance(info.speed, int) assert isinstance(info.mtu, int) @@ -333,7 +324,4 @@ def test_negative_signal(self): p.terminate() code = p.wait() assert code == -signal.SIGTERM - if enum is not None: - assert isinstance(code, enum.IntEnum) - else: - assert isinstance(code, int) + assert isinstance(code, enum.IntEnum) diff --git a/psutil/tests/test_linux.py b/psutil/tests/test_linux.py index 15eaf5e2e..ea96a0a73 100755 --- a/psutil/tests/test_linux.py +++ b/psutil/tests/test_linux.py @@ -6,7 +6,6 @@ """Linux specific tests.""" -from __future__ import division import collections import contextlib @@ -20,12 +19,10 @@ import textwrap import time import warnings +from unittest import mock import psutil from psutil import LINUX -from psutil._compat import PY3 -from psutil._compat import FileNotFoundError -from psutil._compat import basestring from psutil.tests import AARCH64 from psutil.tests import GITHUB_ACTIONS from psutil.tests import GLOBAL_TIMEOUT @@ -41,14 +38,12 @@ from psutil.tests import PsutilTestCase from psutil.tests import ThreadTask from psutil.tests import call_until -from psutil.tests import mock 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 -from psutil.tests import which if LINUX: @@ -73,11 +68,8 @@ def get_ipv4_address(ifname): import fcntl - ifname = ifname[:15] - if PY3: - ifname = bytes(ifname, 'ascii') - s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - with contextlib.closing(s): + 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 @@ -88,11 +80,8 @@ def get_ipv4_address(ifname): def get_ipv4_netmask(ifname): import fcntl - ifname = ifname[:15] - if PY3: - ifname = bytes(ifname, 'ascii') - s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - with contextlib.closing(s): + 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) @@ -103,11 +92,8 @@ def get_ipv4_netmask(ifname): def get_ipv4_broadcast(ifname): import fcntl - ifname = ifname[:15] - if PY3: - ifname = bytes(ifname, 'ascii') - s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - with contextlib.closing(s): + 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) @@ -140,24 +126,12 @@ def get_ipv6_addresses(ifname): def get_mac_address(ifname): import fcntl - ifname = ifname[:15] - if PY3: - ifname = bytes(ifname, 'ascii') - s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - with contextlib.closing(s): + 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) ) - if PY3: - - def ord(x): - return x - - else: - import __builtin__ - - ord = __builtin__.ord - return ''.join(['%02x:' % ord(char) for char in info[18:24]])[:-1] + return ''.join(['%02x:' % char for char in info[18:24]])[:-1] def free_swap(): @@ -223,19 +197,15 @@ def mock_open_content(pairs): def open_mock(name, *args, **kwargs): if name in pairs: content = pairs[name] - if PY3: - if isinstance(content, basestring): - return io.StringIO(content) - else: - return io.BytesIO(content) + if isinstance(content, str): + return io.StringIO(content) else: return io.BytesIO(content) else: return orig_open(name, *args, **kwargs) orig_open = open - patch_point = 'builtins.open' if PY3 else '__builtin__.open' - with mock.patch(patch_point, create=True, side_effect=open_mock) as m: + with mock.patch("builtins.open", create=True, side_effect=open_mock) as m: yield m @@ -252,8 +222,7 @@ def open_mock(name, *args, **kwargs): return orig_open(name, *args, **kwargs) orig_open = open - patch_point = 'builtins.open' if PY3 else '__builtin__.open' - with mock.patch(patch_point, create=True, side_effect=open_mock) as m: + with mock.patch("builtins.open", create=True, side_effect=open_mock) as m: yield m @@ -495,10 +464,7 @@ def test_avail_old_missing_zoneinfo(self): SReclaimable: 346648 kB """).encode() with mock_open_content({"/proc/meminfo": content}): - with mock_open_exception( - "/proc/zoneinfo", - IOError(errno.ENOENT, 'no such file or directory'), - ): + 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 @@ -623,9 +589,7 @@ def test_missing_sin_sout(self): def test_no_vmstat_mocked(self): # see https://github.com/giampaolo/psutil/issues/722 - with mock_open_exception( - "/proc/vmstat", IOError(errno.ENOENT, 'no such file or directory') - ) as m: + with mock_open_exception("/proc/vmstat", FileNotFoundError) as m: with warnings.catch_warnings(record=True) as ws: warnings.simplefilter("always") ret = psutil.swap_memory() @@ -714,14 +678,14 @@ def test_against_sysdev_cpu_num(self): assert psutil.cpu_count() == count @pytest.mark.skipif( - not which("nproc"), reason="nproc utility not available" + 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 which("lscpu"), reason="lscpu utility not available" + not shutil.which("lscpu"), reason="lscpu utility not available" ) def test_against_lscpu(self): out = sh("lscpu -p") @@ -768,7 +732,7 @@ def test_emulate_fallbacks(self): @pytest.mark.skipif(not LINUX, reason="LINUX only") class TestSystemCPUCountCores(PsutilTestCase): @pytest.mark.skipif( - not which("lscpu"), reason="lscpu utility not available" + not shutil.which("lscpu"), reason="lscpu utility not available" ) def test_against_lscpu(self): out = sh("lscpu -p") @@ -861,8 +825,7 @@ def open_mock(name, *args, **kwargs): return orig_open(name, *args, **kwargs) orig_open = open - patch_point = 'builtins.open' if PY3 else '__builtin__.open' - with mock.patch(patch_point, side_effect=open_mock): + 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 @@ -907,8 +870,7 @@ def open_mock(name, *args, **kwargs): return orig_open(name, *args, **kwargs) orig_open = open - patch_point = 'builtins.open' if PY3 else '__builtin__.open' - with mock.patch(patch_point, side_effect=open_mock): + 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 @@ -930,7 +892,7 @@ 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 IOError(errno.ENOENT, "") + raise FileNotFoundError elif name.endswith('/cpuinfo_cur_freq'): return io.BytesIO(b"200000") elif name == '/proc/cpuinfo': @@ -939,8 +901,7 @@ def open_mock(name, *args, **kwargs): return orig_open(name, *args, **kwargs) orig_open = open - patch_point = 'builtins.open' if PY3 else '__builtin__.open' - with mock.patch(patch_point, side_effect=open_mock): + 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 @@ -1007,7 +968,8 @@ def test_ips(self): assert address in get_ipv6_addresses(name) # XXX - not reliable when having virtual NICs installed by Docker. - # @pytest.mark.skipif(not which('ip'), reason="'ip' utility not available") + # @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] @@ -1026,7 +988,7 @@ def test_ips(self): @pytest.mark.skipif(QEMU_USER, reason="QEMU user not supported") class TestSystemNetIfStats(PsutilTestCase): @pytest.mark.skipif( - not which("ifconfig"), reason="ifconfig utility not available" + not shutil.which("ifconfig"), reason="ifconfig utility not available" ) def test_against_ifconfig(self): for name, stats in psutil.net_if_stats().items(): @@ -1046,7 +1008,7 @@ def test_mtu(self): assert stats.mtu == int(f.read().strip()) @pytest.mark.skipif( - not which("ifconfig"), reason="ifconfig utility not available" + not shutil.which("ifconfig"), reason="ifconfig utility not available" ) def test_flags(self): # first line looks like this: @@ -1081,7 +1043,7 @@ def test_flags(self): @pytest.mark.skipif(not LINUX, reason="LINUX only") class TestSystemNetIOCounters(PsutilTestCase): @pytest.mark.skipif( - not which("ifconfig"), reason="ifconfig utility not available" + not shutil.which("ifconfig"), reason="ifconfig utility not available" ) @retry_on_failure() def test_against_ifconfig(self): @@ -1140,7 +1102,7 @@ def test_emulate_ipv6_unsupported(self, supports_ipv6, inet_ntop): s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) self.addCleanup(s.close) s.bind(("::1", 0)) - except socket.error: + except OSError: pass psutil.net_connections(kind='inet6') @@ -1198,7 +1160,7 @@ def test_zfs_fs(self): return # No ZFS partitions on this system. Let's fake one. - fake_file = io.StringIO(u"nodev\tzfs\n") + fake_file = io.StringIO("nodev\tzfs\n") with mock.patch( 'psutil._common.open', return_value=fake_file, create=True ) as m1: @@ -1407,7 +1369,7 @@ def test_comparisons(self): assert base == c @pytest.mark.skipif( - not which("findmnt"), reason="findmnt utility not available" + not shutil.which("findmnt"), reason="findmnt utility not available" ) @pytest.mark.skipif(GITHUB_ACTIONS, reason="unsupported on GITHUB_ACTIONS") def test_against_findmnt(self): @@ -1455,24 +1417,23 @@ def test_no_procfs_on_import(self): def open_mock(name, *args, **kwargs): if name.startswith('/proc'): - raise IOError(errno.ENOENT, 'rejecting access for test') + raise FileNotFoundError return orig_open(name, *args, **kwargs) - patch_point = 'builtins.open' if PY3 else '__builtin__.open' - with mock.patch(patch_point, side_effect=open_mock): + with mock.patch("builtins.open", side_effect=open_mock): reload_module(psutil) - with pytest.raises(IOError): + with pytest.raises(OSError): psutil.cpu_times() - with pytest.raises(IOError): + with pytest.raises(OSError): psutil.cpu_times(percpu=True) - with pytest.raises(IOError): + with pytest.raises(OSError): psutil.cpu_percent() - with pytest.raises(IOError): + with pytest.raises(OSError): psutil.cpu_percent(percpu=True) - with pytest.raises(IOError): + with pytest.raises(OSError): psutil.cpu_times_percent() - with pytest.raises(IOError): + with pytest.raises(OSError): psutil.cpu_times_percent(percpu=True) psutil.PROCFS_PATH = my_procfs @@ -1563,23 +1524,23 @@ def test_procfs_path(self): os.mkdir(tdir) try: psutil.PROCFS_PATH = tdir - with pytest.raises(IOError): + with pytest.raises(OSError): psutil.virtual_memory() - with pytest.raises(IOError): + with pytest.raises(OSError): psutil.cpu_times() - with pytest.raises(IOError): + with pytest.raises(OSError): psutil.cpu_times(percpu=True) - with pytest.raises(IOError): + with pytest.raises(OSError): psutil.boot_time() - # self.assertRaises(IOError, psutil.pids) - with pytest.raises(IOError): + # self.assertRaises(OSError, psutil.pids) + with pytest.raises(OSError): psutil.net_connections() - with pytest.raises(IOError): + with pytest.raises(OSError): psutil.net_io_counters() - with pytest.raises(IOError): + with pytest.raises(OSError): psutil.net_if_stats() - # self.assertRaises(IOError, psutil.disk_io_counters) - with pytest.raises(IOError): + # self.assertRaises(OSError, psutil.disk_io_counters) + with pytest.raises(OSError): psutil.disk_partitions() with pytest.raises(psutil.NoSuchProcess): psutil.Process() @@ -1621,7 +1582,9 @@ def test_pid_exists_no_proc_status(self): @pytest.mark.skipif(not LINUX, reason="LINUX only") @pytest.mark.skipif(not HAS_BATTERY, reason="no battery") class TestSensorsBattery(PsutilTestCase): - @pytest.mark.skipif(not which("acpi"), reason="acpi utility not available") + @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('%', '')) @@ -1637,8 +1600,7 @@ def open_mock(name, *args, **kwargs): return orig_open(name, *args, **kwargs) orig_open = open - patch_point = 'builtins.open' if PY3 else '__builtin__.open' - with mock.patch(patch_point, side_effect=open_mock) as m: + with mock.patch("builtins.open", side_effect=open_mock) as m: assert psutil.sensors_battery().power_plugged is True assert ( psutil.sensors_battery().secsleft @@ -1651,15 +1613,14 @@ def test_emulate_power_plugged_2(self): # case code relies on /status file. def open_mock(name, *args, **kwargs): if name.endswith(('AC0/online', 'AC/online')): - raise IOError(errno.ENOENT, "") + raise FileNotFoundError elif name.endswith("/status"): - return io.StringIO(u"charging") + return io.StringIO("charging") else: return orig_open(name, *args, **kwargs) orig_open = open - patch_point = 'builtins.open' if PY3 else '__builtin__.open' - with mock.patch(patch_point, side_effect=open_mock) as m: + with mock.patch("builtins.open", side_effect=open_mock) as m: assert psutil.sensors_battery().power_plugged is True assert m.called @@ -1672,8 +1633,7 @@ def open_mock(name, *args, **kwargs): return orig_open(name, *args, **kwargs) orig_open = open - patch_point = 'builtins.open' if PY3 else '__builtin__.open' - with mock.patch(patch_point, side_effect=open_mock) as m: + with mock.patch("builtins.open", side_effect=open_mock) as m: assert psutil.sensors_battery().power_plugged is False assert m.called @@ -1682,15 +1642,14 @@ def test_emulate_power_not_plugged_2(self): # case code relies on /status file. def open_mock(name, *args, **kwargs): if name.endswith(('AC0/online', 'AC/online')): - raise IOError(errno.ENOENT, "") + raise FileNotFoundError elif name.endswith("/status"): - return io.StringIO(u"discharging") + return io.StringIO("discharging") else: return orig_open(name, *args, **kwargs) orig_open = open - patch_point = 'builtins.open' if PY3 else '__builtin__.open' - with mock.patch(patch_point, side_effect=open_mock) as m: + with mock.patch("builtins.open", side_effect=open_mock) as m: assert psutil.sensors_battery().power_plugged is False assert m.called @@ -1702,15 +1661,14 @@ def open_mock(name, *args, **kwargs): '/sys/class/power_supply/AC0/online', '/sys/class/power_supply/AC/online', )): - raise IOError(errno.ENOENT, "") + raise FileNotFoundError elif name.startswith("/sys/class/power_supply/BAT0/status"): return io.BytesIO(b"???") else: return orig_open(name, *args, **kwargs) orig_open = open - patch_point = 'builtins.open' if PY3 else '__builtin__.open' - with mock.patch(patch_point, side_effect=open_mock) as m: + with mock.patch("builtins.open", side_effect=open_mock) as m: assert psutil.sensors_battery().power_plugged is None assert m.called @@ -1727,11 +1685,11 @@ def test_emulate_energy_full_not_avail(self): # Expected fallback on /capacity. with mock_open_exception( "/sys/class/power_supply/BAT0/energy_full", - IOError(errno.ENOENT, ""), + FileNotFoundError, ): with mock_open_exception( "/sys/class/power_supply/BAT0/charge_full", - IOError(errno.ENOENT, ""), + FileNotFoundError, ): with mock_open_content( {"/sys/class/power_supply/BAT0/capacity": b"88"} @@ -1741,14 +1699,14 @@ def test_emulate_energy_full_not_avail(self): 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", IOError(errno.ENOENT, "") + "/sys/class/power_supply/AC/online", FileNotFoundError ): with mock_open_exception( - "/sys/class/power_supply/AC0/online", IOError(errno.ENOENT, "") + "/sys/class/power_supply/AC0/online", FileNotFoundError ): with mock_open_exception( "/sys/class/power_supply/BAT0/status", - IOError(errno.ENOENT, ""), + FileNotFoundError, ): assert psutil.sensors_battery().power_plugged is None @@ -1758,18 +1716,17 @@ class TestSensorsBatteryEmulated(PsutilTestCase): def test_it(self): def open_mock(name, *args, **kwargs): if name.endswith("/energy_now"): - return io.StringIO(u"60000000") + return io.StringIO("60000000") elif name.endswith("/power_now"): - return io.StringIO(u"0") + return io.StringIO("0") elif name.endswith("/energy_full"): - return io.StringIO(u"60000001") + return io.StringIO("60000001") else: return orig_open(name, *args, **kwargs) orig_open = open - patch_point = 'builtins.open' if PY3 else '__builtin__.open' with mock.patch('os.listdir', return_value=["BAT0"]) as mlistdir: - with mock.patch(patch_point, side_effect=open_mock) as mopen: + with mock.patch("builtins.open", side_effect=open_mock) as mopen: assert psutil.sensors_battery() is not None assert mlistdir.called assert mopen.called @@ -1780,9 +1737,9 @@ class TestSensorsTemperatures(PsutilTestCase): def test_emulate_class_hwmon(self): def open_mock(name, *args, **kwargs): if name.endswith('/name'): - return io.StringIO(u"name") + return io.StringIO("name") elif name.endswith('/temp1_label'): - return io.StringIO(u"label") + return io.StringIO("label") elif name.endswith('/temp1_input'): return io.BytesIO(b"30000") elif name.endswith('/temp1_max'): @@ -1793,8 +1750,7 @@ def open_mock(name, *args, **kwargs): return orig_open(name, *args, **kwargs) orig_open = open - patch_point = 'builtins.open' if PY3 else '__builtin__.open' - with mock.patch(patch_point, side_effect=open_mock): + 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'] @@ -1812,9 +1768,9 @@ def open_mock(name, *args, **kwargs): elif name.endswith('temp'): return io.BytesIO(b"30000") elif name.endswith('0_type'): - return io.StringIO(u"critical") + return io.StringIO("critical") elif name.endswith('type'): - return io.StringIO(u"name") + return io.StringIO("name") else: return orig_open(name, *args, **kwargs) @@ -1833,8 +1789,7 @@ def glob_mock(path): return [] orig_open = open - patch_point = 'builtins.open' if PY3 else '__builtin__.open' - with mock.patch(patch_point, side_effect=open_mock): + 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 == '' # noqa @@ -1848,17 +1803,16 @@ class TestSensorsFans(PsutilTestCase): def test_emulate_data(self): def open_mock(name, *args, **kwargs): if name.endswith('/name'): - return io.StringIO(u"name") + return io.StringIO("name") elif name.endswith('/fan1_label'): - return io.StringIO(u"label") + return io.StringIO("label") elif name.endswith('/fan1_input'): - return io.StringIO(u"2000") + return io.StringIO("2000") else: return orig_open(name, *args, **kwargs) orig_open = open - patch_point = 'builtins.open' if PY3 else '__builtin__.open' - with mock.patch(patch_point, side_effect=open_mock): + with mock.patch("builtins.open", side_effect=open_mock): with mock.patch( 'glob.glob', return_value=['/sys/class/hwmon/hwmon2/fan1'] ): @@ -1946,14 +1900,13 @@ def get_test_file(fname): assert get_test_file(testfn).mode == "r+" with open(testfn, "a+"): assert get_test_file(testfn).mode == "a+" - # note: "x" bit is not supported - if PY3: - 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+" + + 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() @@ -1965,7 +1918,7 @@ def test_open_files_file_gone(self): call_until(lambda: len(p.open_files()) != len(files)) with mock.patch( 'psutil._pslinux.os.readlink', - side_effect=OSError(errno.ENOENT, ""), + side_effect=FileNotFoundError, ) as m: assert p.open_files() == [] assert m.called @@ -1987,9 +1940,8 @@ def test_open_files_fd_gone(self): 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 = 'builtins.open' if PY3 else '__builtin__.open' with mock.patch( - patch_point, side_effect=IOError(errno.ENOENT, "") + "builtins.open", side_effect=FileNotFoundError ) as m: assert p.open_files() == [] assert m.called @@ -2031,13 +1983,13 @@ def test_terminal_mocked(self): def test_cmdline_mocked(self): # see: https://github.com/giampaolo/psutil/issues/639 p = psutil.Process() - fake_file = io.StringIO(u'foo\x00bar\x00') + 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(u'foo\x00bar\x00\x00') + fake_file = io.StringIO('foo\x00bar\x00\x00') with mock.patch( 'psutil._common.open', return_value=fake_file, create=True ) as m: @@ -2047,13 +1999,13 @@ def test_cmdline_mocked(self): def test_cmdline_spaces_mocked(self): # see: https://github.com/giampaolo/psutil/issues/1179 p = psutil.Process() - fake_file = io.StringIO(u'foo bar ') + 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(u'foo bar ') + fake_file = io.StringIO('foo bar ') with mock.patch( 'psutil._common.open', return_value=fake_file, create=True ) as m: @@ -2064,7 +2016,7 @@ def test_cmdline_mixed_separators(self): # https://github.com/giampaolo/psutil/issues/ # 1179#issuecomment-552984549 p = psutil.Process() - fake_file = io.StringIO(u'foo\x20bar\x00') + fake_file = io.StringIO('foo\x20bar\x00') with mock.patch( 'psutil._common.open', return_value=fake_file, create=True ) as m: @@ -2085,13 +2037,12 @@ def test_threads_mocked(self): # of raising NSP. def open_mock_1(name, *args, **kwargs): if name.startswith('/proc/%s/task' % os.getpid()): - raise IOError(errno.ENOENT, "") + raise FileNotFoundError else: return orig_open(name, *args, **kwargs) orig_open = open - patch_point = 'builtins.open' if PY3 else '__builtin__.open' - with mock.patch(patch_point, side_effect=open_mock_1) as m: + with mock.patch("builtins.open", side_effect=open_mock_1) as m: ret = psutil.Process().threads() assert m.called assert ret == [] @@ -2100,17 +2051,17 @@ def open_mock_1(name, *args, **kwargs): # exception. def open_mock_2(name, *args, **kwargs): if name.startswith('/proc/%s/task' % os.getpid()): - raise IOError(errno.EPERM, "") + raise PermissionError else: return orig_open(name, *args, **kwargs) - with mock.patch(patch_point, side_effect=open_mock_2): + 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=OSError(errno.ENOENT, "") + 'psutil._pslinux.readlink', side_effect=FileNotFoundError ) as m: # de-activate guessing from cmdline() with mock.patch( @@ -2124,7 +2075,7 @@ 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( - '/proc/%s/smaps' % os.getpid(), IOError(errno.ENOENT, "") + '/proc/%s/smaps' % os.getpid(), FileNotFoundError ) as m: p = psutil.Process() with pytest.raises(FileNotFoundError): @@ -2146,7 +2097,7 @@ def test_rlimit_zombie(self): # happen in case of zombie process: # https://travis-ci.org/giampaolo/psutil/jobs/51368273 with mock.patch( - "psutil._pslinux.prlimit", side_effect=OSError(errno.ENOSYS, "") + "resource.prlimit", side_effect=OSError(errno.ENOSYS, "") ) as m1: with mock.patch( "psutil._pslinux.Process._is_zombie", return_value=True diff --git a/psutil/tests/test_memleaks.py b/psutil/tests/test_memleaks.py index e249ca516..fd4cd09a4 100755 --- a/psutil/tests/test_memleaks.py +++ b/psutil/tests/test_memleaks.py @@ -14,7 +14,6 @@ because of how its JIT handles memory, so tests are skipped. """ -from __future__ import print_function import functools import os @@ -28,8 +27,6 @@ from psutil import POSIX from psutil import SUNOS from psutil import WINDOWS -from psutil._compat import ProcessLookupError -from psutil._compat import super from psutil.tests import HAS_CPU_AFFINITY from psutil.tests import HAS_CPU_FREQ from psutil.tests import HAS_ENVIRON diff --git a/psutil/tests/test_misc.py b/psutil/tests/test_misc.py index cf98f8b4b..6771dbf98 100755 --- a/psutil/tests/test_misc.py +++ b/psutil/tests/test_misc.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be @@ -9,13 +8,15 @@ import ast import collections -import errno +import contextlib +import io import json import os import pickle import socket import stat import sys +from unittest import mock import psutil import psutil.tests @@ -30,9 +31,6 @@ from psutil._common import parse_environ_block from psutil._common import supports_ipv6 from psutil._common import wrap_numbers -from psutil._compat import PY3 -from psutil._compat import FileNotFoundError -from psutil._compat import redirect_stderr from psutil.tests import CI_TESTING from psutil.tests import HAS_BATTERY from psutil.tests import HAS_MEMORY_MAPS @@ -45,7 +43,6 @@ from psutil.tests import QEMU_USER from psutil.tests import SCRIPTS_DIR from psutil.tests import PsutilTestCase -from psutil.tests import mock from psutil.tests import process_namespace from psutil.tests import pytest from psutil.tests import reload_module @@ -202,7 +199,7 @@ def test_process__eq__(self): assert p1 != 'foo' def test_process__hash__(self): - s = set([psutil.Process(), psutil.Process()]) + s = {psutil.Process(), psutil.Process()} assert len(s) == 1 @@ -217,7 +214,6 @@ def test__all__(self): for name in dir_psutil: if name in { 'debug', - 'long', 'tests', 'test', 'PermissionError', @@ -240,7 +236,7 @@ def test__all__(self): # 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 on python 3 + # 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 @@ -338,10 +334,6 @@ def check(ret): assert b.pid == 4567 assert b.name == 'name' - # # XXX: https://github.com/pypa/setuptools/pull/2896 - # @pytest.mark.skipif(APPVEYOR, - # reason="temporarily disabled due to setuptools bug" - # ) # def test_setup_script(self): # setup_py = os.path.join(ROOT_DIR, 'setup.py') # if CI_TESTING and not os.path.exists(setup_py): @@ -587,7 +579,7 @@ def test_supports_ipv6(self): supports_ipv6.cache_clear() with mock.patch( - 'psutil._common.socket.socket', side_effect=socket.error + 'psutil._common.socket.socket', side_effect=OSError ) as s: assert not supports_ipv6() assert s.called @@ -609,7 +601,7 @@ def test_supports_ipv6(self): supports_ipv6.cache_clear() assert s.called else: - with pytest.raises(socket.error): + with pytest.raises(OSError): sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) try: sock.bind(("::1", 0)) @@ -620,31 +612,19 @@ 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=OSError(errno.EPERM, "foo") - ): + 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=OSError(errno.EACCES, "foo") - ): - with pytest.raises(OSError): - isfile_strict(this_file) - with mock.patch( - 'psutil._common.os.stat', side_effect=OSError(errno.ENOENT, "foo") + '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): - if PY3: - from io import StringIO - else: - from StringIO import StringIO - with mock.patch.object(psutil._common, "PSUTIL_DEBUG", True): - with redirect_stderr(StringIO()) as f: + with contextlib.redirect_stderr(io.StringIO()) as f: debug("hello") sys.stderr.flush() msg = f.getvalue() @@ -654,7 +634,7 @@ def test_debug(self): # supposed to use repr(exc) with mock.patch.object(psutil._common, "PSUTIL_DEBUG", True): - with redirect_stderr(StringIO()) as f: + with contextlib.redirect_stderr(io.StringIO()) as f: debug(ValueError("this is an error")) msg = f.getvalue() assert "ignoring ValueError" in msg @@ -662,7 +642,7 @@ def test_debug(self): # supposed to use str(exc), because of extra info about file name with mock.patch.object(psutil._common, "PSUTIL_DEBUG", True): - with redirect_stderr(StringIO()) as f: + with contextlib.redirect_stderr(io.StringIO()) as f: exc = OSError(2, "no such file") exc.filename = "/foo" debug(exc) @@ -838,7 +818,7 @@ def test_cache_wrap(self): assert cache[1] == { 'disk_io': {('disk1', 0): 0, ('disk1', 1): 0, ('disk1', 2): 100} } - assert cache[2] == {'disk_io': {'disk1': set([('disk1', 2)])}} + assert cache[2] == {'disk_io': {'disk1': {('disk1', 2)}}} def check_cache_info(): cache = wrap_numbers.cache_info() @@ -849,7 +829,7 @@ def check_cache_info(): ('disk1', 2): 100, } } - assert cache[2] == {'disk_io': {'disk1': set([('disk1', 2)])}} + assert cache[2] == {'disk_io': {'disk1': {('disk1', 2)}}} # then it remains the same input = {'disk1': nt(100, 100, 10)} @@ -873,7 +853,7 @@ def check_cache_info(): assert cache[1] == { 'disk_io': {('disk1', 0): 0, ('disk1', 1): 0, ('disk1', 2): 190} } - assert cache[2] == {'disk_io': {'disk1': set([('disk1', 2)])}} + assert cache[2] == {'disk_io': {'disk1': {('disk1', 2)}}} def test_cache_changing_keys(self): input = {'disk1': nt(5, 5, 5)} @@ -949,7 +929,7 @@ def assert_stdout(exe, *args, **kwargs): @staticmethod def assert_syntax(exe): exe = os.path.join(SCRIPTS_DIR, exe) - with open(exe, encoding="utf8") if PY3 else open(exe) as f: + with open(exe, encoding="utf8") as f: src = f.read() ast.parse(src) diff --git a/psutil/tests/test_posix.py b/psutil/tests/test_posix.py index 551eaec50..6c8ac7f49 100755 --- a/psutil/tests/test_posix.py +++ b/psutil/tests/test_posix.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be @@ -11,8 +10,10 @@ import errno import os import re +import shutil import subprocess import time +from unittest import mock import psutil from psutil import AIX @@ -27,14 +28,12 @@ from psutil.tests import PYTHON_EXE from psutil.tests import QEMU_USER from psutil.tests import PsutilTestCase -from psutil.tests import mock 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 -from psutil.tests import which if POSIX: @@ -288,7 +287,7 @@ def test_exe(self): assert ps_pathname == psutil_pathname except AssertionError: # certain platforms such as BSD are more accurate returning: - # "/usr/local/bin/python2.7" + # "/usr/local/bin/python3.7" # ...instead of: # "/usr/local/bin/python" # We do not want to consider this difference in accuracy @@ -348,7 +347,7 @@ def test_pids(self): # 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 which('ifconfig'), reason="no ifconfig cmd") + @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") diff --git a/psutil/tests/test_process.py b/psutil/tests/test_process.py index 76dcbbf32..35432b1db 100755 --- a/psutil/tests/test_process.py +++ b/psutil/tests/test_process.py @@ -7,8 +7,10 @@ """Tests for psutil.Process class.""" import collections +import contextlib import errno import getpass +import io import itertools import os import signal @@ -20,6 +22,7 @@ import textwrap import time import types +from unittest import mock import psutil from psutil import AIX @@ -32,12 +35,6 @@ from psutil import POSIX from psutil import WINDOWS from psutil._common import open_text -from psutil._compat import PY3 -from psutil._compat import FileNotFoundError -from psutil._compat import long -from psutil._compat import redirect_stderr -from psutil._compat import super -from psutil.tests import APPVEYOR from psutil.tests import CI_TESTING from psutil.tests import GITHUB_ACTIONS from psutil.tests import GLOBAL_TIMEOUT @@ -60,7 +57,6 @@ 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 mock from psutil.tests import process_namespace from psutil.tests import pytest from psutil.tests import reap_children @@ -130,16 +126,12 @@ def test_send_signal(self): def test_send_signal_mocked(self): sig = signal.SIGTERM p = self.spawn_psproc() - with mock.patch( - 'psutil.os.kill', side_effect=OSError(errno.ESRCH, "") - ): + 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=OSError(errno.EPERM, "") - ): + with mock.patch('psutil.os.kill', side_effect=PermissionError): with pytest.raises(psutil.AccessDenied): p.send_signal(sig) @@ -354,10 +346,7 @@ def test_io_counters(self): # test writes io1 = p.io_counters() with open(self.get_testfn(), 'wb') as f: - if PY3: - f.write(bytes("x" * 1000000, 'ascii')) - else: - f.write("x" * 1000000) + f.write(bytes("x" * 1000000, 'ascii')) io2 = p.io_counters() assert io2.write_count >= io1.write_count assert io2.write_bytes >= io1.write_bytes @@ -498,10 +487,10 @@ def test_rlimit(self): f.write(b"X" * 1024) # write() or flush() doesn't always cause the exception # but close() will. - with pytest.raises(IOError) as exc: + with pytest.raises(OSError) as exc: with open(testfn, "wb") as f: f.write(b"X" * 1025) - assert (exc.value.errno if PY3 else exc.value[0]) == errno.EFBIG + assert exc.value.errno == errno.EFBIG finally: p.rlimit(psutil.RLIMIT_FSIZE, (soft, hard)) assert p.rlimit(psutil.RLIMIT_FSIZE) == (soft, hard) @@ -689,7 +678,7 @@ def test_memory_maps(self): if fname in {'addr', 'perms'}: assert value, value else: - assert isinstance(value, (int, long)) + assert isinstance(value, int) assert value >= 0, value @pytest.mark.skipif(not HAS_MEMORY_MAPS, reason="not supported") @@ -734,7 +723,7 @@ def test_exe(self): assert normcase(exe) == normcase(PYTHON_EXE) else: # certain platforms such as BSD are more accurate returning: - # "/usr/local/bin/python2.7" + # "/usr/local/bin/python3.7" # ...instead of: # "/usr/local/bin/python" # We do not want to consider this difference in accuracy @@ -811,7 +800,6 @@ def test_name(self): @pytest.mark.skipif(PYPY or QEMU_USER, reason="unreliable on PYPY") @pytest.mark.skipif(QEMU_USER, reason="unreliable on QEMU user") - @pytest.mark.skipif(MACOS and not PY3, reason="broken MACOS + PY2") def test_long_name(self): pyexe = create_py_exe(self.get_testfn(suffix=string.digits * 2)) cmdline = [ @@ -842,9 +830,7 @@ def test_long_name(self): # @pytest.mark.skipif(SUNOS, reason="broken on SUNOS") # @pytest.mark.skipif(AIX, reason="broken on AIX") # @pytest.mark.skipif(PYPY, reason="broken on PYPY") - # @pytest.mark.skipif(SUNOS, reason="broken on SUNOS") - # @pytest.mark.skipif(MACOS and not PY3, reason="broken MACOS + PY2") - # @retry_on_failure() + # @pytest.mark.skipif(QEMU_USER, reason="broken on QEMU user") # def test_prog_w_funky_name(self): # # Test that name(), exe() and cmdline() correctly handle programs # # with funky chars such as spaces and ")", see: @@ -868,9 +854,8 @@ def test_uids(self): assert real == os.getuid() # os.geteuid() refers to "effective" uid assert effective == os.geteuid() - # No such thing as os.getsuid() ("saved" uid), but starting - # from python 2.7 we have os.getresuid() which returns all - # of them. + # 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() @@ -882,9 +867,8 @@ def test_gids(self): assert real == os.getgid() # os.geteuid() refers to "effective" uid assert effective == os.getegid() - # No such thing as os.getsgid() ("saved" gid), but starting - # from python 2.7 we have os.getresgid() which returns all - # of them. + # 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() @@ -1066,8 +1050,6 @@ def test_cpu_affinity_all_combinations(self): # TODO: #595 @pytest.mark.skipif(BSD, reason="broken on BSD") - # can't find any process file on Appveyor - @pytest.mark.skipif(APPVEYOR, reason="unreliable on APPVEYOR") def test_open_files(self): p = psutil.Process() testfn = self.get_testfn() @@ -1107,8 +1089,6 @@ def test_open_files(self): # TODO: #595 @pytest.mark.skipif(BSD, reason="broken on BSD") - # can't find any process file on Appveyor - @pytest.mark.skipif(APPVEYOR, reason="unreliable on APPVEYOR") def test_open_files_2(self): # test fd and path fields p = psutil.Process() @@ -1441,11 +1421,6 @@ def test_zombie_process_status_w_exc(self): def test_reused_pid(self): # Emulate a case where PID has been reused by another process. - if PY3: - from io import StringIO - else: - from StringIO import StringIO - subp = self.spawn_testproc() p = psutil.Process(subp.pid) p._ident = (p.pid, p.create_time() + 100) @@ -1457,7 +1432,7 @@ def test_reused_pid(self): # make sure is_running() removed PID from process_iter() # internal cache with mock.patch.object(psutil._common, "PSUTIL_DEBUG", True): - with redirect_stderr(StringIO()) as f: + with contextlib.redirect_stderr(io.StringIO()) as f: list(psutil.process_iter()) assert ( "refreshing Process instance for reused PID %s" % p.pid @@ -1544,13 +1519,12 @@ def clean_dict(d): ]) for name in exclude: d.pop(name, None) - return dict([ - ( - k.replace("\r", "").replace("\n", ""), - v.replace("\r", "").replace("\n", ""), - ) + return { + k.replace("\r", "").replace("\n", ""): v.replace( + "\r", "" + ).replace("\n", "") for k, v in d.items() - ]) + } self.maxDiff = None p = psutil.Process() @@ -1677,7 +1651,7 @@ def tearDownClass(cls): reap_children() def test_misc(self): - # XXX this test causes a ResourceWarning on Python 3 because + # XXX this test causes a ResourceWarning because # psutil.__subproc instance doesn't get properly freed. # Not sure what to do though. cmd = [ diff --git a/psutil/tests/test_process_all.py b/psutil/tests/test_process_all.py index 780ea194b..29f3f894e 100755 --- a/psutil/tests/test_process_all.py +++ b/psutil/tests/test_process_all.py @@ -27,10 +27,6 @@ from psutil import OSX from psutil import POSIX from psutil import WINDOWS -from psutil._compat import PY3 -from psutil._compat import FileNotFoundError -from psutil._compat import long -from psutil._compat import unicode from psutil.tests import CI_TESTING from psutil.tests import PYTEST_PARALLEL from psutil.tests import QEMU_USER @@ -161,7 +157,7 @@ def cmdline(self, ret, info): assert isinstance(part, str) def exe(self, ret, info): - assert isinstance(ret, (str, unicode)) + assert isinstance(ret, str) assert ret.strip() == ret if ret: if WINDOWS and not ret.endswith('.exe'): @@ -184,12 +180,12 @@ def pid(self, ret, info): assert ret >= 0 def ppid(self, ret, info): - assert isinstance(ret, (int, long)) + assert isinstance(ret, int) assert ret >= 0 proc_info(ret) def name(self, ret, info): - assert isinstance(ret, (str, unicode)) + 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 @@ -245,7 +241,7 @@ def status(self, ret, info): def io_counters(self, ret, info): assert is_namedtuple(ret) for field in ret: - assert isinstance(field, (int, long)) + assert isinstance(field, int) if field != -1: assert field >= 0 @@ -306,7 +302,7 @@ def cpu_num(self, ret, info): def memory_info(self, ret, info): assert is_namedtuple(ret) for value in ret: - assert isinstance(value, (int, long)) + assert isinstance(value, int) assert value >= 0 if WINDOWS: assert ret.peak_wset >= ret.wset @@ -319,7 +315,7 @@ def memory_full_info(self, ret, info): total = psutil.virtual_memory().total for name in ret._fields: value = getattr(ret, name) - assert isinstance(value, (int, long)) + 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 @@ -368,7 +364,7 @@ def net_connections(self, ret, info): check_connection_ntuple(conn) def cwd(self, ret, info): - assert isinstance(ret, (str, unicode)) + assert isinstance(ret, str) assert ret.strip() == ret if ret: assert os.path.isabs(ret), ret @@ -423,7 +419,7 @@ def memory_maps(self, ret, info): if not WINDOWS: assert value, repr(value) else: - assert isinstance(value, (int, long)) + assert isinstance(value, int) assert value >= 0 def num_handles(self, ret, info): @@ -441,15 +437,12 @@ def nice(self, ret, info): if x.endswith('_PRIORITY_CLASS') ] assert ret in priorities - if PY3: - assert isinstance(ret, enum.IntEnum) - else: - assert isinstance(ret, int) + assert isinstance(ret, enum.IntEnum) def num_ctx_switches(self, ret, info): assert is_namedtuple(ret) for value in ret: - assert isinstance(value, (int, long)) + assert isinstance(value, int) assert value >= 0 def rlimit(self, ret, info): diff --git a/psutil/tests/test_system.py b/psutil/tests/test_system.py index 0b69ada78..1e814b1e8 100755 --- a/psutil/tests/test_system.py +++ b/psutil/tests/test_system.py @@ -6,8 +6,8 @@ """Tests for system APIS.""" -import contextlib import datetime +import enum import errno import os import platform @@ -17,6 +17,7 @@ import socket import sys import time +from unittest import mock import psutil from psutil import AIX @@ -29,9 +30,6 @@ from psutil import POSIX from psutil import SUNOS from psutil import WINDOWS -from psutil._compat import PY3 -from psutil._compat import FileNotFoundError -from psutil._compat import long from psutil.tests import ASCII_FS from psutil.tests import CI_TESTING from psutil.tests import DEVNULL @@ -51,8 +49,6 @@ from psutil.tests import UNICODE_SUFFIX from psutil.tests import PsutilTestCase from psutil.tests import check_net_address -from psutil.tests import enum -from psutil.tests import mock from psutil.tests import pytest from psutil.tests import retry_on_failure @@ -194,7 +190,7 @@ def test_2(procs, callback): sproc1.terminate() sproc2.terminate() gone, alive = test_2(procs, callback) - assert set(pids) == set([sproc1.pid, sproc2.pid, sproc3.pid]) + assert set(pids) == {sproc1.pid, sproc2.pid, sproc3.pid} for p in gone: assert hasattr(p, 'returncode') @@ -335,7 +331,7 @@ def test_virtual_memory(self): for name in mem._fields: value = getattr(mem, name) if name != 'percent': - assert isinstance(value, (int, long)) + assert isinstance(value, int) if name != 'total': if not value >= 0: raise self.fail("%r < 0 (%s)" % (name, value)) @@ -605,7 +601,7 @@ def check_ls(ls): assert nt.current <= nt.max for name in nt._fields: value = getattr(nt, name) - assert isinstance(value, (int, long, float)) + assert isinstance(value, (int, float)) assert value >= 0 ls = psutil.cpu_freq(percpu=True) @@ -823,7 +819,7 @@ def test_net_if_addrs(self): # self.assertEqual(sorted(nics.keys()), # sorted(psutil.net_io_counters(pernic=True).keys())) - families = set([socket.AF_INET, socket.AF_INET6, psutil.AF_LINK]) + 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) @@ -833,14 +829,12 @@ def test_net_if_addrs(self): assert isinstance(addr.netmask, (str, type(None))) assert isinstance(addr.broadcast, (str, type(None))) assert addr.family in families - if PY3 and not PYPY: - assert isinstance(addr.family, enum.IntEnum) + 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: - s = socket.socket(addr.family) - with contextlib.closing(s): + with socket.socket(addr.family) as s: s.bind((addr.address, 0)) elif addr.family == socket.AF_INET6: info = socket.getaddrinfo( @@ -852,8 +846,7 @@ def test_net_if_addrs(self): socket.AI_PASSIVE, )[0] af, socktype, proto, _canonname, sa = info - s = socket.socket(af, socktype, proto) - with contextlib.closing(s): + with socket.socket(af, socktype, proto) as s: s.bind(sa) for ip in ( addr.address, @@ -981,5 +974,5 @@ def test_sensors_fans(self): assert isinstance(name, str) for entry in entries: assert isinstance(entry.label, str) - assert isinstance(entry.current, (int, long)) + assert isinstance(entry.current, int) assert entry.current >= 0 diff --git a/psutil/tests/test_testutils.py b/psutil/tests/test_testutils.py index 1c83a94c6..e6c3afa85 100755 --- a/psutil/tests/test_testutils.py +++ b/psutil/tests/test_testutils.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be @@ -8,7 +7,6 @@ """Tests for testing utils (psutil.tests namespace).""" import collections -import contextlib import errno import os import socket @@ -17,6 +15,7 @@ import textwrap import unittest import warnings +from unittest import mock import psutil import psutil.tests @@ -26,7 +25,6 @@ from psutil._common import open_binary from psutil._common import open_text from psutil._common import supports_ipv6 -from psutil._compat import PY3 from psutil.tests import CI_TESTING from psutil.tests import COVERAGE from psutil.tests import HAS_NET_CONNECTIONS_UNIX @@ -44,7 +42,6 @@ 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 mock from psutil.tests import process_namespace from psutil.tests import pytest from psutil.tests import reap_children @@ -159,7 +156,7 @@ def test_wait_for_file_empty(self): 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(IOError): + with pytest.raises(OSError): wait_for_file(testfn) def test_wait_for_file_no_delete(self): @@ -298,14 +295,13 @@ def test_terminate(self): class TestNetUtils(PsutilTestCase): def bind_socket(self): port = get_free_port() - with contextlib.closing(bind_socket(addr=('', port))) as s: + 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() - sock = bind_unix_socket(name) - with contextlib.closing(sock): + with bind_unix_socket(name) as sock: assert sock.family == socket.AF_UNIX assert sock.type == socket.SOCK_STREAM assert sock.getsockname() == name @@ -313,20 +309,17 @@ def test_bind_unix_socket(self): assert stat.S_ISSOCK(os.stat(name).st_mode) # UDP name = self.get_testfn() - sock = bind_unix_socket(name, type=socket.SOCK_DGRAM) - with contextlib.closing(sock): + 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 contextlib.closing(server): - with contextlib.closing(client): - # Ensure they are connected and the positions are - # correct. - assert server.getsockname() == addr - assert client.getpeername() == addr - assert client.getsockname() != 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( @@ -507,7 +500,6 @@ def foo(self): assert result.wasSuccessful() assert len(result.skipped) == 0 - @pytest.mark.skipif(not PY3, reason="not PY3") def test_skip(self): class TestCase(unittest.TestCase): def foo(self): diff --git a/psutil/tests/test_unicode.py b/psutil/tests/test_unicode.py index c03aabd8f..d8a8c4bfc 100755 --- a/psutil/tests/test_unicode.py +++ b/psutil/tests/test_unicode.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be @@ -20,13 +19,8 @@ * 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: - * Python 3: sys.getfilesystemencodeerrors() (PY 3.6+) or - "surrogatescape" on POSIX and "replace" on Windows - * Python 2: "replace" -* on Python 2 all APIs return bytes (str type), never unicode -* on Python 2, you can go back to unicode by doing: - - >>> unicode(p.exe(), sys.getdefaultencoding(), errors="replace") + * sys.getfilesystemencodeerrors() or "surrogatescape" on POSIX and + "replace" on Windows. For a detailed explanation of how psutil handles unicode see #1040. @@ -74,18 +68,13 @@ import os import shutil -import traceback import warnings from contextlib import closing import psutil from psutil import BSD -from psutil import MACOS from psutil import POSIX from psutil import WINDOWS -from psutil._compat import PY3 -from psutil._compat import super -from psutil.tests import APPVEYOR from psutil.tests import ASCII_FS from psutil.tests import CI_TESTING from psutil.tests import HAS_ENVIRON @@ -109,27 +98,6 @@ from psutil.tests import terminate -if APPVEYOR: - - def safe_rmpath(path): # NOQA - # TODO - this is quite random and I'm not sure why it happens, - # nor I can reproduce it locally: - # https://ci.appveyor.com/project/giampaolo/psutil/build/job/ - # jiq2cgd6stsbtn60 - # safe_rmpath() happens after reap_children() so this is weird - # Perhaps wait_procs() on Windows is broken? Maybe because - # of STILL_ACTIVE? - # https://github.com/giampaolo/psutil/blob/ - # 68c7a70728a31d8b8b58f4be6c4c0baa2f449eda/psutil/arch/ - # windows/process_info.c#L146 - from psutil.tests import safe_rmpath as rm - - try: - return rm(path) - except WindowsError: - traceback.print_exc() - - def try_unicode(suffix): """Return True if both the fs and the subprocess module can deal with a unicode file name. @@ -142,7 +110,7 @@ def try_unicode(suffix): sproc = spawn_testproc(cmd=[testfn]) shutil.copyfile(testfn, testfn + '-2') safe_rmpath(testfn + '-2') - except (UnicodeEncodeError, IOError): + except (UnicodeEncodeError, OSError): return False else: return True @@ -180,23 +148,18 @@ def setUp(self): @pytest.mark.xdist_group(name="serial") @pytest.mark.skipif(ASCII_FS, reason="ASCII fs") -@pytest.mark.skipif(PYPY and not PY3, reason="too much trouble on PYPY2") class TestFSAPIs(BaseUnicodeTest): """Test FS APIs with a funky, valid, UTF8 path name.""" funky_suffix = UNICODE_SUFFIX def expect_exact_path_match(self): - # Do not expect psutil to correctly handle unicode paths on - # Python 2 if os.listdir() is not able either. - here = '.' if isinstance(self.funky_name, str) else u'.' with warnings.catch_warnings(): warnings.simplefilter("ignore") - return self.funky_name in os.listdir(here) + return self.funky_name in os.listdir(".") # --- - @pytest.mark.skipif(MACOS and not PY3, reason="broken MACOS + PY2") def test_proc_exe(self): cmd = [ self.funky_name, @@ -222,7 +185,6 @@ def test_proc_name(self): if self.expect_exact_path_match(): assert name == os.path.basename(self.funky_name) - @pytest.mark.skipif(MACOS and not PY3, reason="broken MACOS + PY2") def test_proc_cmdline(self): cmd = [ self.funky_name, @@ -265,13 +227,7 @@ def test_proc_open_files(self): @pytest.mark.skipif(not POSIX, reason="POSIX only") def test_proc_net_connections(self): name = self.get_testfn(suffix=self.funky_suffix) - try: - sock = bind_unix_socket(name) - except UnicodeEncodeError: - if PY3: - raise - else: - raise pytest.skip("not supported") + sock = bind_unix_socket(name) with closing(sock): conn = psutil.Process().net_connections('unix')[0] assert isinstance(conn.laddr, str) @@ -290,13 +246,7 @@ def find_sock(cons): raise ValueError("connection not found") name = self.get_testfn(suffix=self.funky_suffix) - try: - sock = bind_unix_socket(name) - except UnicodeEncodeError: - if PY3: - raise - else: - raise pytest.skip("not supported") + sock = bind_unix_socket(name) with closing(sock): cons = psutil.net_connections(kind='unix') conn = find_sock(cons) @@ -310,13 +260,8 @@ def test_disk_usage(self): psutil.disk_usage(dname) @pytest.mark.skipif(not HAS_MEMORY_MAPS, reason="not supported") - @pytest.mark.skipif( - not PY3, reason="ctypes does not support unicode on PY2" - ) @pytest.mark.skipif(PYPY, reason="unstable on PYPY") def test_memory_maps(self): - # XXX: on Python 2, using ctypes.CDLL with a unicode path - # opens a message box which blocks the test run. with copyload_shared_lib(suffix=self.funky_suffix) as funky_path: def normpath(p): @@ -339,7 +284,6 @@ class TestFSAPIsWithInvalidPath(TestFSAPIs): funky_suffix = INVALID_UNICODE_SUFFIX def expect_exact_path_match(self): - # Invalid unicode names are supposed to work on Python 2. return True @@ -351,16 +295,13 @@ def expect_exact_path_match(self): class TestNonFSAPIS(BaseUnicodeTest): """Unicode tests for non fs-related APIs.""" - funky_suffix = UNICODE_SUFFIX if PY3 else 'è' + 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. On Python 2 subprocess module is broken as - # it's not able to handle with non-ASCII env vars, so - # we use "è", which is part of the extended ASCII table - # (unicode point <= 255). + # with fs paths. env = os.environ.copy() env['FUNNY_ARG'] = self.funky_suffix sproc = self.spawn_testproc(env=env) diff --git a/psutil/tests/test_windows.py b/psutil/tests/test_windows.py index 161e2f35d..4e5d2484d 100755 --- a/psutil/tests/test_windows.py +++ b/psutil/tests/test_windows.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: UTF-8 -* # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be @@ -8,32 +7,27 @@ """Windows specific tests.""" import datetime -import errno 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._compat import FileNotFoundError -from psutil._compat import super -from psutil._compat import which -from psutil.tests import APPVEYOR from psutil.tests import GITHUB_ACTIONS from psutil.tests import HAS_BATTERY from psutil.tests import IS_64BIT -from psutil.tests import PY3 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 mock from psutil.tests import pytest from psutil.tests import retry_on_failure from psutil.tests import sh @@ -58,10 +52,6 @@ @pytest.mark.skipif(not WINDOWS, reason="WINDOWS only") @pytest.mark.skipif(PYPY, reason="pywin32 not available on PYPY") -# https://github.com/giampaolo/psutil/pull/1762#issuecomment-632892692 -@pytest.mark.skipif( - GITHUB_ACTIONS and not PY3, reason="pywin32 broken on GITHUB + PY2" -) class WindowsTestCase(PsutilTestCase): pass @@ -72,7 +62,7 @@ def powershell(cmd): >>> powershell( "Get-CIMInstance Win32_PageFileUsage | Select AllocatedBaseSize") """ - if not which("powershell.exe"): + if not shutil.which("powershell.exe"): raise pytest.skip("powershell.exe not available") cmdline = ( 'powershell.exe -ExecutionPolicy Bypass -NoLogo -NonInteractive ' @@ -199,13 +189,12 @@ def test_percent_swapmem(self): # time.localtime(p.create_time())) # Note: this test is not very reliable - @pytest.mark.skipif(APPVEYOR, reason="test not relieable on appveyor") @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 = set([x.ProcessId for x in w]) + wmi_pids = {x.ProcessId for x in w} psutil_pids = set(psutil.pids()) assert wmi_pids == psutil_pids @@ -563,7 +552,7 @@ def test_num_handles(self): def test_error_partial_copy(self): # https://github.com/giampaolo/psutil/issues/875 - exc = WindowsError() + exc = OSError() exc.winerror = 299 with mock.patch("psutil._psplatform.cext.proc_cwd", side_effect=exc): with mock.patch("time.sleep") as m: @@ -676,7 +665,7 @@ def test_memory_info(self): mem_1 = psutil.Process(self.pid).memory_info() with mock.patch( "psutil._psplatform.cext.proc_memory_info", - side_effect=OSError(errno.EPERM, "msg"), + side_effect=PermissionError, ) as fun: mem_2 = psutil.Process(self.pid).memory_info() assert len(mem_1) == len(mem_2) @@ -690,7 +679,7 @@ def test_create_time(self): ctime = psutil.Process(self.pid).create_time() with mock.patch( "psutil._psplatform.cext.proc_times", - side_effect=OSError(errno.EPERM, "msg"), + side_effect=PermissionError, ) as fun: assert psutil.Process(self.pid).create_time() == ctime assert fun.called @@ -699,7 +688,7 @@ def test_cpu_times(self): cpu_times_1 = psutil.Process(self.pid).cpu_times() with mock.patch( "psutil._psplatform.cext.proc_times", - side_effect=OSError(errno.EPERM, "msg"), + side_effect=PermissionError, ) as fun: cpu_times_2 = psutil.Process(self.pid).cpu_times() assert fun.called @@ -710,7 +699,7 @@ 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=OSError(errno.EPERM, "msg"), + side_effect=PermissionError, ) as fun: io_counters_2 = psutil.Process(self.pid).io_counters() for i in range(len(io_counters_1)): @@ -721,7 +710,7 @@ def test_num_handles(self): num_handles = psutil.Process(self.pid).num_handles() with mock.patch( "psutil._psplatform.cext.proc_num_handles", - side_effect=OSError(errno.EPERM, "msg"), + side_effect=PermissionError, ) as fun: assert psutil.Process(self.pid).num_handles() == num_handles assert fun.called @@ -838,7 +827,7 @@ def test_environ_64(self): @pytest.mark.skipif(not WINDOWS, reason="WINDOWS only") class TestServices(PsutilTestCase): def test_win_service_iter(self): - valid_statuses = set([ + valid_statuses = { "running", "paused", "start", @@ -846,9 +835,9 @@ def test_win_service_iter(self): "continue", "stop", "stopped", - ]) - valid_start_types = set(["automatic", "manual", "disabled"]) - valid_statuses = set([ + } + valid_start_types = {"automatic", "manual", "disabled"} + valid_statuses = { "running", "paused", "start_pending", @@ -856,7 +845,7 @@ def test_win_service_iter(self): "continue_pending", "stop_pending", "stopped", - ]) + } for serv in psutil.win_service_iter(): data = serv.as_dict() assert isinstance(data['name'], str) @@ -894,11 +883,8 @@ def test_win_service_get(self): # test NoSuchProcess service = psutil.win_service_get(name) - if PY3: - args = (0, "msg", 0, ERROR_SERVICE_DOES_NOT_EXIST) - else: - args = (ERROR_SERVICE_DOES_NOT_EXIST, "msg") - exc = WindowsError(*args) + exc = OSError(0, "msg", 0) + exc.winerror = ERROR_SERVICE_DOES_NOT_EXIST with mock.patch( "psutil._psplatform.cext.winservice_query_status", side_effect=exc ): @@ -911,11 +897,8 @@ def test_win_service_get(self): service.username() # test AccessDenied - if PY3: - args = (0, "msg", 0, ERROR_ACCESS_DENIED) - else: - args = (ERROR_ACCESS_DENIED, "msg") - exc = WindowsError(*args) + exc = OSError(0, "msg", 0) + exc.winerror = ERROR_ACCESS_DENIED with mock.patch( "psutil._psplatform.cext.winservice_query_status", side_effect=exc ): diff --git a/pyproject.toml b/pyproject.toml index fea968df4..b64adb6ff 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,9 +33,8 @@ ignore = [ "ARG001", # unused-function-argument "ARG002", # unused-method-argument "B007", # Loop control variable `x` not used within loop body - "B904", # Within an `except` clause, raise exceptions with `raise ... from err` (PYTHON2.7 COMPAT) - "B904", # Use `raise from` to specify exception cause (PYTHON2.7 COMPAT) - "C4", # flake8-comprehensions (PYTHON2.7 COMPAT) + "B904", # Use `raise from` to specify exception cause + "C4", # flake8-comprehensions "C90", # mccabe (function `X` is too complex) "COM812", # Trailing comma missing "D", # pydocstyle @@ -44,14 +43,13 @@ ignore = [ "ERA001", # Found commented-out code "FBT", # flake8-boolean-trap (makes zero sense) "FIX", # Line contains TODO / XXX / ..., consider resolving the issue - "FLY", # flynt (PYTHON2.7 COMPAT) + "FLY002", # static-join-to-f-string / Consider {expression} instead of string join "FURB101", # `open` and `read` should be replaced by `Path(src).read_text()` "FURB103", # `open` and `write` should be replaced by `Path(src).write_text()` "FURB113", # Use `ls.extend(...)` instead of repeatedly calling `ls.append()` "FURB116", # [*] Replace `hex` call with `f"{start:x}"` "FURB118", # [*] Use `operator.add` instead of defining a lambda "FURB140", # [*] Use `itertools.starmap` instead of the generator - "FURB145", # [*] Prefer `copy` method over slicing (PYTHON2.7 COMPAT) "FURB192", # [*] Prefer `min` over `sorted()` to compute the minimum value in a sequence "INP", # flake8-no-pep420 "N801", # Class name `async_chat` should use CapWords convention (ASYNCORE COMPAT) @@ -91,14 +89,7 @@ ignore = [ "TD", # all TODOs, XXXs, etc. "TRY300", # Consider moving this statement to an `else` block "TRY301", # Abstract `raise` to an inner function - "UP009", # [*] UTF-8 encoding declaration is unnecessary (PYTHON2.7 COMPAT) - "UP010", # [*] Unnecessary `__future__` import `print_function` for target Python version (PYTHON2.7 COMPAT) - "UP024", # [*] Replace aliased errors with `OSError` (PYTHON2.7 COMPAT) - "UP025", # [*] Remove unicode literals from strings (PYTHON2.7 COMPAT) - "UP028", # [*] Replace `yield` over `for` loop with `yield from` (PYTHON2.7 COMPAT) "UP031", # [*] Use format specifiers instead of percent format - "UP032", # [*] Use f-string instead of `format` call (PYTHON2.7 COMPAT) - "UP036", # Version block is outdated for minimum Python version (PYTHON2.7 COMPAT) ] [tool.ruff.lint.per-file-ignores] @@ -106,7 +97,6 @@ ignore = [ # EM101 == raw-string-in-exception # TRY003 == raise-vanilla-args ".github/workflows/*" = ["T201", "T203"] -"psutil/_compat.py" = ["PLW0127"] # self-assigning-variable "psutil/tests/*" = ["EM101", "TRY003"] "scripts/*" = ["T201", "T203"] "scripts/internal/*" = ["EM101", "T201", "T203", "TRY003"] @@ -119,7 +109,6 @@ lines-after-imports = 2 [tool.coverage.report] exclude_lines = [ - "enum.IntEnum", "except ImportError:", "globals().update", "if BSD", @@ -129,22 +118,16 @@ exclude_lines = [ "if MACOS", "if NETBSD", "if OPENBSD", - "if PY3:", "if SUNOS", "if WINDOWS", "if _WINDOWS:", "if __name__ == .__main__.:", - "if enum is None:", - "if enum is not None:", - "if has_enums:", "if ppid_map is None:", "if sys.platform.startswith", - "import enum", "pragma: no cover", "raise NotImplementedError", ] omit = [ - "psutil/_compat.py", "psutil/tests/*", "setup.py", ] @@ -190,7 +173,6 @@ disable = [ [tool.vulture] exclude = [ "docs/conf.py", - "psutil/_compat.py", "psutil/tests/", "scripts/internal/winmake.py", ] diff --git a/scripts/battery.py b/scripts/battery.py index 0595d1ad1..d9a783daa 100755 --- a/scripts/battery.py +++ b/scripts/battery.py @@ -13,7 +13,6 @@ plugged in: no """ -from __future__ import print_function import sys diff --git a/scripts/cpu_distribution.py b/scripts/cpu_distribution.py index bfbb14b6c..85495c0d3 100755 --- a/scripts/cpu_distribution.py +++ b/scripts/cpu_distribution.py @@ -38,15 +38,14 @@ kwork """ -from __future__ import print_function import collections import os +import shutil import sys import time import psutil -from psutil._compat import get_terminal_size if not hasattr(psutil.Process, "cpu_num"): @@ -97,7 +96,7 @@ def main(): print("%-10s" % pname[:10], end="") print() curr_line += 1 - if curr_line >= get_terminal_size()[1]: + if curr_line >= shutil.get_terminal_size()[1]: break time.sleep(1) diff --git a/scripts/fans.py b/scripts/fans.py index a9a8b8e67..bfab434c4 100755 --- a/scripts/fans.py +++ b/scripts/fans.py @@ -11,7 +11,6 @@ cpu_fan 3200 RPM """ -from __future__ import print_function import sys diff --git a/scripts/ifconfig.py b/scripts/ifconfig.py index e23472ba9..dd7684e87 100755 --- a/scripts/ifconfig.py +++ b/scripts/ifconfig.py @@ -42,7 +42,6 @@ broadcast : ff:ff:ff:ff:ff:ff """ -from __future__ import print_function import socket diff --git a/scripts/internal/appveyor_run_with_compiler.cmd b/scripts/internal/appveyor_run_with_compiler.cmd deleted file mode 100644 index 7965f865f..000000000 --- a/scripts/internal/appveyor_run_with_compiler.cmd +++ /dev/null @@ -1,89 +0,0 @@ -:: To build extensions for 64 bit Python 3, we need to configure environment -:: variables to use the MSVC 2010 C++ compilers from GRMSDKX_EN_DVD.iso of: -:: MS Windows SDK for Windows 7 and .NET Framework 4 (SDK v7.1) -:: -:: To build extensions for 64 bit Python 2, we need to configure environment -:: variables to use the MSVC 2008 C++ compilers from GRMSDKX_EN_DVD.iso of: -:: MS Windows SDK for Windows 7 and .NET Framework 3.5 (SDK v7.0) -:: -:: 32 bit builds, and 64-bit builds for 3.5 and beyond, do not require specific -:: environment configurations. -:: -:: Note: this script needs to be run with the /E:ON and /V:ON flags for the -:: cmd interpreter, at least for (SDK v7.0) -:: -:: More details at: -:: https://github.com/cython/cython/wiki/64BitCythonExtensionsOnWindows -:: http://stackoverflow.com/a/13751649/163740 -:: -:: Author: Olivier Grisel -:: License: CC0 1.0 Universal: http://creativecommons.org/publicdomain/zero/1.0/ -:: -:: Notes about batch files for Python people: -:: -:: Quotes in values are literally part of the values: -:: SET FOO="bar" -:: FOO is now five characters long: " b a r " -:: If you don't want quotes, don't include them on the right-hand side. -:: -:: The CALL lines at the end of this file look redundant, but if you move them -:: outside of the IF clauses, they do not run properly in the SET_SDK_64==Y -:: case, I don't know why. - -@ECHO OFF - -SET COMMAND_TO_RUN=%* -SET WIN_SDK_ROOT=C:\Program Files\Microsoft SDKs\Windows -SET WIN_WDK=c:\Program Files (x86)\Windows Kits\10\Include\wdf - -:: Extract the major and minor versions, and allow for the minor version to be -:: more than 9. This requires the version number to have two dots in it. -SET MAJOR_PYTHON_VERSION=%PYTHON_VERSION:~0,1% -IF "%PYTHON_VERSION:~3,1%" == "." ( - SET MINOR_PYTHON_VERSION=%PYTHON_VERSION:~2,1% -) ELSE ( - SET MINOR_PYTHON_VERSION=%PYTHON_VERSION:~2,2% -) - -:: Based on the Python version, determine what SDK version to use, and whether -:: to set the SDK for 64-bit. -IF %MAJOR_PYTHON_VERSION% == 2 ( - SET WINDOWS_SDK_VERSION="v7.0" - SET SET_SDK_64=Y -) ELSE ( - IF %MAJOR_PYTHON_VERSION% == 3 ( - SET WINDOWS_SDK_VERSION="v7.1" - IF %MINOR_PYTHON_VERSION% LEQ 4 ( - SET SET_SDK_64=Y - ) ELSE ( - SET SET_SDK_64=N - IF EXIST "%WIN_WDK%" ( - :: See: https://connect.microsoft.com/VisualStudio/feedback/details/1610302/ - REN "%WIN_WDK%" 0wdf - ) - ) - ) ELSE ( - ECHO Unsupported Python version: "%MAJOR_PYTHON_VERSION%" - EXIT 1 - ) -) - -IF %PYTHON_ARCH% == 64 ( - IF %SET_SDK_64% == Y ( - ECHO Configuring Windows SDK %WINDOWS_SDK_VERSION% for Python %MAJOR_PYTHON_VERSION% on a 64 bit architecture - SET DISTUTILS_USE_SDK=1 - SET MSSdk=1 - "%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Setup\WindowsSdkVer.exe" -q -version:%WINDOWS_SDK_VERSION% - "%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Bin\SetEnv.cmd" /x64 /release - ECHO Executing: %COMMAND_TO_RUN% - call %COMMAND_TO_RUN% || EXIT 1 - ) ELSE ( - ECHO Using default MSVC build environment for 64 bit architecture - ECHO Executing: %COMMAND_TO_RUN% - call %COMMAND_TO_RUN% || EXIT 1 - ) -) ELSE ( - ECHO Using default MSVC build environment for 32 bit architecture - ECHO Executing: %COMMAND_TO_RUN% - call %COMMAND_TO_RUN% || EXIT 1 -) diff --git a/scripts/internal/bench_oneshot.py b/scripts/internal/bench_oneshot.py index b58224bb6..43c279a30 100755 --- a/scripts/internal/bench_oneshot.py +++ b/scripts/internal/bench_oneshot.py @@ -9,8 +9,6 @@ See: https://github.com/giampaolo/psutil/issues/799. """ -from __future__ import division -from __future__ import print_function import sys import textwrap diff --git a/scripts/internal/check_broken_links.py b/scripts/internal/check_broken_links.py index 90cb258c3..ed84385ef 100755 --- a/scripts/internal/check_broken_links.py +++ b/scripts/internal/check_broken_links.py @@ -38,7 +38,6 @@ Author: Himanshu Shekhar (2017) """ -from __future__ import print_function import argparse import concurrent.futures @@ -93,7 +92,7 @@ def sanitize_url(url): def find_urls(s): matches = REGEX.findall(s) or [] - return list(set([sanitize_url(x) for x in matches])) + return list({sanitize_url(x) for x in matches}) def parse_rst(fname): diff --git a/scripts/internal/clinter.py b/scripts/internal/clinter.py index 516ca894e..225140018 100755 --- a/scripts/internal/clinter.py +++ b/scripts/internal/clinter.py @@ -6,7 +6,6 @@ """A super simple linter to check C syntax.""" -from __future__ import print_function import argparse import sys diff --git a/scripts/internal/download_wheels_github.py b/scripts/internal/download_wheels.py similarity index 78% rename from scripts/internal/download_wheels_github.py rename to scripts/internal/download_wheels.py index c6ecb5dda..bd9e74390 100755 --- a/scripts/internal/download_wheels_github.py +++ b/scripts/internal/download_wheels.py @@ -22,14 +22,12 @@ import requests -from psutil import __version__ from psutil._common import bytes2human from psutil.tests import safe_rmpath USER = "giampaolo" PROJECT = "psutil" -PROJECT_VERSION = __version__ OUTFILE = "wheels-github.zip" TOKEN = "" TIMEOUT = 30 @@ -60,27 +58,12 @@ def download_zip(url): print("got %s, size %s)" % (OUTFILE, bytes2human(totbytes))) -def rename_win27_wheels(): - # See: https://github.com/giampaolo/psutil/issues/810 - src = 'dist/psutil-%s-cp27-cp27m-win32.whl' % PROJECT_VERSION - dst = 'dist/psutil-%s-cp27-none-win32.whl' % PROJECT_VERSION - if os.path.exists(src): - print("rename: %s\n %s" % (src, dst)) - os.rename(src, dst) - src = 'dist/psutil-%s-cp27-cp27m-win_amd64.whl' % PROJECT_VERSION - dst = 'dist/psutil-%s-cp27-none-win_amd64.whl' % PROJECT_VERSION - if os.path.exists(src): - print("rename: %s\n %s" % (src, dst)) - os.rename(src, dst) - - def run(): data = get_artifacts() download_zip(data['artifacts'][0]['archive_download_url']) os.makedirs('dist', exist_ok=True) with zipfile.ZipFile(OUTFILE, 'r') as zf: zf.extractall('dist') - rename_win27_wheels() def main(): diff --git a/scripts/internal/download_wheels_appveyor.py b/scripts/internal/download_wheels_appveyor.py deleted file mode 100755 index 154faeed7..000000000 --- a/scripts/internal/download_wheels_appveyor.py +++ /dev/null @@ -1,115 +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. - -"""Script which downloads wheel files hosted on AppVeyor: -https://ci.appveyor.com/project/giampaolo/psutil -Re-adapted from the original recipe of Ibarra Corretge' -: -http://code.saghul.net/index.php/2015/09/09/. -""" - -from __future__ import print_function - -import concurrent.futures -import os -import sys - -import requests - -from psutil import __version__ -from psutil._common import bytes2human -from psutil._common import print_color - - -USER = "giampaolo" -PROJECT = "psutil" -PROJECT_VERSION = __version__ -BASE_URL = 'https://ci.appveyor.com/api' -PY_VERSIONS = ['2.7'] -TIMEOUT = 30 - - -def download_file(url): - local_fname = url.split('/')[-1] - local_fname = os.path.join('dist', local_fname) - os.makedirs('dist', exist_ok=True) - r = requests.get(url, stream=True, timeout=TIMEOUT) - with open(local_fname, 'wb') as f: - for chunk in r.iter_content(chunk_size=16384): - if chunk: # filter out keep-alive new chunks - f.write(chunk) - return local_fname - - -def get_file_urls(): - with requests.Session() as session: - data = session.get( - BASE_URL + '/projects/' + USER + '/' + PROJECT, timeout=TIMEOUT - ) - data = data.json() - - urls = [] - for job in (job['jobId'] for job in data['build']['jobs']): - job_url = BASE_URL + '/buildjobs/' + job + '/artifacts' - data = session.get(job_url, timeout=TIMEOUT) - data = data.json() - for item in data: - file_url = job_url + '/' + item['fileName'] - urls.append(file_url) - if not urls: - print_color("no artifacts found", 'red') - sys.exit(1) - else: - for url in sorted(urls, key=os.path.basename): - yield url - - -def rename_win27_wheels(): - # See: https://github.com/giampaolo/psutil/issues/810 - src = 'dist/psutil-%s-cp27-cp27m-win32.whl' % PROJECT_VERSION - dst = 'dist/psutil-%s-cp27-none-win32.whl' % PROJECT_VERSION - print("rename: %s\n %s" % (src, dst)) - os.rename(src, dst) - src = 'dist/psutil-%s-cp27-cp27m-win_amd64.whl' % PROJECT_VERSION - dst = 'dist/psutil-%s-cp27-none-win_amd64.whl' % PROJECT_VERSION - print("rename: %s\n %s" % (src, dst)) - os.rename(src, dst) - - -def run(): - urls = get_file_urls() - completed = 0 - exc = None - with concurrent.futures.ThreadPoolExecutor() as e: - fut_to_url = {e.submit(download_file, url): url for url in urls} - for fut in concurrent.futures.as_completed(fut_to_url): - url = fut_to_url[fut] - try: - local_fname = fut.result() - except Exception: - print_color("error while downloading %s" % (url), 'red') - raise - else: - completed += 1 - print( - "downloaded %-45s %s" - % (local_fname, bytes2human(os.path.getsize(local_fname))) - ) - # 2 wheels (32 and 64 bit) per supported python version - expected = len(PY_VERSIONS) * 2 - if expected != completed: - return sys.exit("expected %s files, got %s" % (expected, completed)) - if exc: - return sys.exit() - rename_win27_wheels() - - -def main(): - run() - - -if __name__ == '__main__': - main() diff --git a/scripts/internal/generate_manifest.py b/scripts/internal/generate_manifest.py index 9eeeb1a24..1e261e5a4 100755 --- a/scripts/internal/generate_manifest.py +++ b/scripts/internal/generate_manifest.py @@ -12,7 +12,7 @@ SKIP_EXTS = ('.png', '.jpg', '.jpeg', '.svg') -SKIP_FILES = 'appveyor.yml' +SKIP_FILES = () SKIP_PREFIXES = ('.ci/', '.github/') diff --git a/scripts/internal/git_pre_commit.py b/scripts/internal/git_pre_commit.py index 1441a1454..de4b461e6 100755 --- a/scripts/internal/git_pre_commit.py +++ b/scripts/internal/git_pre_commit.py @@ -10,7 +10,6 @@ "make install-git-hooks". """ -from __future__ import print_function import os import shlex @@ -19,7 +18,6 @@ PYTHON = sys.executable -PY3 = sys.version_info[0] >= 3 def term_supports_colors(): @@ -74,11 +72,6 @@ def sh(cmd): return stdout -def open_text(path): - kw = {'encoding': 'utf8'} if PY3 else {} - return open(path, **kw) - - def git_commit_files(): out = sh(["git", "diff", "--cached", "--name-only"]) py_files = [ @@ -158,7 +151,7 @@ def main(): toml_sort(toml_files) if new_rm_mv: out = sh([PYTHON, "scripts/internal/generate_manifest.py"]) - with open_text('MANIFEST.in') as f: + with open("MANIFEST.in", encoding="utf8") as f: if out.strip() != f.read().strip(): sys.exit( "some files were added, deleted or renamed; " diff --git a/scripts/internal/install_pip.py b/scripts/internal/install_pip.py index 44557529a..9b1ee7a21 100755 --- a/scripts/internal/install_pip.py +++ b/scripts/internal/install_pip.py @@ -18,18 +18,10 @@ import os import ssl import tempfile +from urllib.request import urlopen -PY3 = sys.version_info[0] >= 3 -if PY3: - from urllib.request import urlopen - - URL = "https://bootstrap.pypa.io/get-pip.py" - -else: - from urllib2 import urlopen - - URL = "https://bootstrap.pypa.io/pip/2.7/get-pip.py" +URL = "https://bootstrap.pypa.io/get-pip.py" def main(): diff --git a/scripts/internal/print_access_denied.py b/scripts/internal/print_access_denied.py index f7017b04d..6bb0cdd08 100755 --- a/scripts/internal/print_access_denied.py +++ b/scripts/internal/print_access_denied.py @@ -44,8 +44,6 @@ Totals: access-denied=1744, calls=10020, processes=334 """ -from __future__ import division -from __future__ import print_function import time from collections import defaultdict diff --git a/scripts/internal/print_announce.py b/scripts/internal/print_announce.py index 65e28e351..8b62f2b71 100755 --- a/scripts/internal/print_announce.py +++ b/scripts/internal/print_announce.py @@ -49,8 +49,7 @@ line tools such as: ps, top, lsof, netstat, ifconfig, who, df, kill, free, \ nice, ionice, iostat, iotop, uptime, pidof, tty, taskset, pmap. It \ currently supports Linux, Windows, macOS, Sun Solaris, FreeBSD, OpenBSD, \ -NetBSD and AIX. Supported Python versions are 2.7 and 3.6+. PyPy is also \ -known to work. +NetBSD and AIX. Supported Python versions are cPython 3.6+ and PyPy. What's new ========== diff --git a/scripts/internal/print_api_speed.py b/scripts/internal/print_api_speed.py index 786644b5a..3fecbfbcd 100755 --- a/scripts/internal/print_api_speed.py +++ b/scripts/internal/print_api_speed.py @@ -68,8 +68,6 @@ memory_maps 300 0.74281 """ -from __future__ import division -from __future__ import print_function import argparse import inspect diff --git a/scripts/internal/print_downloads.py b/scripts/internal/print_downloads.py index 8afb7c1fd..ae6216907 100755 --- a/scripts/internal/print_downloads.py +++ b/scripts/internal/print_downloads.py @@ -11,7 +11,6 @@ * https://hugovk.github.io/top-pypi-packages/. """ -from __future__ import print_function import json import os @@ -117,7 +116,7 @@ def downloads_by_distro(): def print_row(left, right): if isinstance(right, int): - right = '{:,}'.format(right) + right = f'{right:,}' print(templ % (left, right)) diff --git a/scripts/internal/print_timeline.py b/scripts/internal/print_timeline.py index 706601943..6dec932b1 100755 --- a/scripts/internal/print_timeline.py +++ b/scripts/internal/print_timeline.py @@ -24,7 +24,7 @@ def sh(cmd): def get_tag_date(tag): - out = sh(r"git log -1 --format=%ai {}".format(tag)) + out = sh(f"git log -1 --format=%ai {tag}") return out.split(' ')[0] diff --git a/scripts/internal/test_python2_setup_py.py b/scripts/internal/test_python2_setup_py.py new file mode 100755 index 000000000..612184459 --- /dev/null +++ b/scripts/internal/test_python2_setup_py.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python2 + +# 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. + + +"""Invoke setup.py with Python 2.7, make sure it fails but not due to +SyntaxError, and that it prints a meaningful error message. +""" + +import os +import subprocess +import sys + + +ROOT_DIR = os.path.realpath( + os.path.join(os.path.dirname(__file__), "..", "..") +) + + +def main(): + if sys.version_info[:2] != (2, 7): + raise RuntimeError("this script is supposed to be run with python 2.7") + setup_py = os.path.join(ROOT_DIR, "setup.py") + p = subprocess.Popen( + [sys.executable, setup_py], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + stdout, stderr = p.communicate() + assert p.wait() == 1 + assert not stdout, stdout + assert "psutil no longer supports Python 2.7" in stderr, stderr + assert "Latest version supporting Python 2.7 is" in stderr, stderr + + +if __name__ == "__main__": + main() diff --git a/scripts/internal/winmake.py b/scripts/internal/winmake.py index a0b085280..2e076f0df 100755 --- a/scripts/internal/winmake.py +++ b/scripts/internal/winmake.py @@ -11,7 +11,6 @@ that they should be deemed illegal! """ -from __future__ import print_function import argparse import atexit @@ -25,9 +24,7 @@ import sys -APPVEYOR = bool(os.environ.get('APPVEYOR')) -PYTHON = sys.executable if APPVEYOR else os.getenv('PYTHON', sys.executable) -PY3 = sys.version_info[0] >= 3 +PYTHON = os.getenv('PYTHON', sys.executable) PYTEST_ARGS = ["-v", "-s", "--tb=short"] HERE = os.path.abspath(os.path.dirname(__file__)) ROOT_DIR = os.path.realpath(os.path.join(HERE, "..", "..")) @@ -42,8 +39,6 @@ DEV_DEPS = setup.DEV_DEPS _cmds = {} -if PY3: - basestring = str GREEN = 2 LIGHTBLUE = 3 @@ -61,9 +56,8 @@ def safe_print(text, file=sys.stdout): """Prints a (unicode) string to the console, encoded depending on the stdout/file encoding (eg. cp437 on Windows). This is to avoid encoding errors in case of funky path names. - Works with Python 2 and 3. """ - if not isinstance(text, basestring): + if not isinstance(text, str): return print(text, file=file) try: file.write(text) @@ -181,15 +175,13 @@ def build(): # order to allow "import psutil" when using the interactive interpreter # from within psutil root directory. cmd = [PYTHON, "setup.py", "build_ext", "-i"] - if sys.version_info[:2] >= (3, 6) and (os.cpu_count() or 1) > 1: + if os.cpu_count() or 1 > 1: # noqa: PLR0133 cmd += ['--parallel', str(os.cpu_count())] # Print coloured warnings in real time. p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) try: for line in iter(p.stdout.readline, b''): - if PY3: - line = line.decode() - line = line.strip() + line = line.decode().strip() if 'warning' in line: win_colorprint(line, YELLOW) elif 'error' in line: @@ -454,18 +446,6 @@ def print_sysinfo(): print_sysinfo() -def download_appveyor_wheels(): - """Download appveyor wheels.""" - sh([ - PYTHON, - "scripts\\internal\\download_wheels_appveyor.py", - "--user", - "giampaolo", - "--project", - "psutil", - ]) - - def generate_manifest(): """Generate MANIFEST.in file.""" script = "scripts\\internal\\generate_manifest.py" @@ -482,8 +462,6 @@ def get_python(path): # try to look for a python installation given a shortcut name path = path.replace('.', '') vers = ( - '27', - '27-64', '310-64', '311-64', '312-64', @@ -504,7 +482,6 @@ def parse_args(): sp.add_parser('build', help="build") sp.add_parser('clean', help="deletes dev files") sp.add_parser('coverage', help="run coverage tests.") - sp.add_parser('download-appveyor-wheels', help="download wheels.") sp.add_parser('generate-manifest', help="generate MANIFEST.in file") sp.add_parser('help', help="print this help") sp.add_parser('install', help="build + install in develop/edit mode") diff --git a/scripts/iotop.py b/scripts/iotop.py index 23c3a376e..2d92f4e2d 100755 --- a/scripts/iotop.py +++ b/scripts/iotop.py @@ -71,7 +71,7 @@ def poll(interval): """ # first get a list of all processes and disk io counters procs = list(psutil.process_iter()) - for p in procs[:]: + for p in procs.copy(): try: p._before = p.io_counters() except psutil.Error: @@ -83,7 +83,7 @@ def poll(interval): time.sleep(interval) # then retrieve the same info again - for p in procs[:]: + for p in procs.copy(): with p.oneshot(): try: p._after = p.io_counters() diff --git a/scripts/pidof.py b/scripts/pidof.py index 662d5d657..7ac8e0323 100755 --- a/scripts/pidof.py +++ b/scripts/pidof.py @@ -11,7 +11,6 @@ 1140 1138 1136 1134 1133 1129 1127 1125 1121 1120 1119 """ -from __future__ import print_function import sys diff --git a/scripts/pmap.py b/scripts/pmap.py index 577e4b294..719ce0134 100755 --- a/scripts/pmap.py +++ b/scripts/pmap.py @@ -9,9 +9,9 @@ $ python3 scripts/pmap.py 32402 Address RSS Mode Mapping -0000000000400000 1200K r-xp /usr/bin/python2.7 -0000000000838000 4K r--p /usr/bin/python2.7 -0000000000839000 304K rw-p /usr/bin/python2.7 +0000000000400000 1200K r-xp /usr/bin/python3.7 +0000000000838000 4K r--p /usr/bin/python3.7 +0000000000839000 304K rw-p /usr/bin/python3.7 00000000008ae000 68K rw-p [anon] 000000000275e000 5396K rw-p [heap] 00002b29bb1e0000 124K r-xp /lib/x86_64-linux-gnu/ld-2.17.so @@ -28,15 +28,15 @@ ... """ +import shutil import sys import psutil from psutil._common import bytes2human -from psutil._compat import get_terminal_size def safe_print(s): - s = s[: get_terminal_size()[0]] + s = s[: shutil.get_terminal_size()[0]] try: print(s) except UnicodeEncodeError: diff --git a/scripts/procsmem.py b/scripts/procsmem.py index eec5cd51a..c8eaf3407 100755 --- a/scripts/procsmem.py +++ b/scripts/procsmem.py @@ -35,7 +35,6 @@ """ -from __future__ import print_function import sys diff --git a/scripts/ps.py b/scripts/ps.py index f07b56865..8478bbd1a 100755 --- a/scripts/ps.py +++ b/scripts/ps.py @@ -32,11 +32,11 @@ """ import datetime +import shutil import time import psutil from psutil._common import bytes2human -from psutil._compat import get_terminal_size def main(): @@ -109,7 +109,7 @@ def main(): cputime, cmdline, ) - print(line[: get_terminal_size()[0]]) + print(line[: shutil.get_terminal_size()[0]]) if __name__ == '__main__': diff --git a/scripts/pstree.py b/scripts/pstree.py index e873e467d..51e3ee6cb 100755 --- a/scripts/pstree.py +++ b/scripts/pstree.py @@ -27,7 +27,6 @@ ... """ -from __future__ import print_function import collections import sys diff --git a/scripts/sensors.py b/scripts/sensors.py index 668cca0a2..861a47d79 100755 --- a/scripts/sensors.py +++ b/scripts/sensors.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be @@ -28,7 +27,6 @@ plugged in: yes """ -from __future__ import print_function import psutil diff --git a/scripts/temperatures.py b/scripts/temperatures.py index 118ebc265..2253fef7f 100755 --- a/scripts/temperatures.py +++ b/scripts/temperatures.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be @@ -22,7 +21,6 @@ Core 3 54.0 °C (high = 100.0 °C, critical = 100.0 °C) """ -from __future__ import print_function import sys diff --git a/setup.py b/setup.py index 727677c49..148bfcfdf 100755 --- a/setup.py +++ b/setup.py @@ -4,9 +4,12 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -"""Cross-platform lib for process and system monitoring in Python.""" +"""Cross-platform lib for process and system monitoring in Python. -from __future__ import print_function +NOTE: the syntax of this script MUST be kept compatible with Python 2.7. +""" + +from __future__ import print_function # noqa: UP010 import ast import contextlib @@ -21,9 +24,21 @@ import sys import sysconfig import tempfile +import textwrap import warnings +if sys.version_info[0] == 2: # noqa: UP036 + sys.exit(textwrap.dedent("""\ + As of version 7.0.0 psutil no longer supports Python 2.7, see: + https://github.com/giampaolo/psutil/issues/2480 + Latest version supporting Python 2.7 is psutil 6.1.X. + Install it with: + + python2 -m pip install psutil==6.1.*\ + """)) + + with warnings.catch_warnings(): warnings.simplefilter("ignore") try: @@ -37,9 +52,10 @@ from distutils.core import Extension from distutils.core import setup + HERE = os.path.abspath(os.path.dirname(__file__)) -# ...so we can import _common.py and _compat.py +# ...so we can import _common.py sys.path.insert(0, os.path.join(HERE, "psutil")) from _common import AIX # NOQA @@ -53,8 +69,6 @@ from _common import SUNOS # NOQA from _common import WINDOWS # NOQA from _common import hilite # NOQA -from _compat import PY3 # NOQA -from _compat import which # NOQA PYPY = '__pypy__' in sys.builtin_module_names @@ -66,23 +80,12 @@ # Test deps, installable via `pip install .[test]` or # `make install-pydeps-test`. -if PY3: - TEST_DEPS = [ - "pytest", - "pytest-xdist", - "setuptools", - ] -else: - TEST_DEPS = [ - "futures", - "ipaddress", - "enum34", - "mock==1.0.1", - "pytest-xdist", - "pytest==4.6.11", - "setuptools", - "unittest2", - ] +TEST_DEPS = [ + "pytest", + "pytest-xdist", + "setuptools", +] + if WINDOWS and not PYPY: TEST_DEPS.append("pywin32") TEST_DEPS.append("wheel") @@ -90,7 +93,7 @@ # Development deps, installable via `pip install .[dev]` or # `make install-pydeps-dev`. -DEV_DEPS = [ +DEV_DEPS = TEST_DEPS + [ "abi3audit", "black", "check-manifest", @@ -111,6 +114,7 @@ "vulture", "wheel", ] + if WINDOWS: DEV_DEPS.append("pyreadline") DEV_DEPS.append("pdbpp") @@ -121,7 +125,7 @@ if BSD: macros.append(("PSUTIL_BSD", 1)) -# Needed to determine _Py_PARSE_PID in case it's missing (Python 2, PyPy). +# Needed to determine _Py_PARSE_PID in case it's missing (PyPy). # Taken from Lib/test/test_fcntl.py. # XXX: not bullet proof as the (long long) case is missing. if struct.calcsize('l') <= 8: @@ -203,7 +207,7 @@ def write(self, s): def missdeps(cmdline): s = "psutil could not be installed from sources" - if not SUNOS and not which("gcc"): + if not SUNOS and not shutil.which("gcc"): s += " because gcc is not installed. " else: s += ". Perhaps Python header files are not installed. " @@ -518,8 +522,6 @@ def main(): 'Operating System :: POSIX :: SunOS/Solaris', 'Operating System :: POSIX', 'Programming Language :: C', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', @@ -545,7 +547,7 @@ def main(): } kwargs.update( python_requires=( - ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" + "!=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" ), extras_require=extras_require, zip_safe=False, @@ -563,19 +565,16 @@ def main(): ("build", "install", "sdist", "bdist", "develop") ) ): - py3 = "3" if PY3 else "" if LINUX: pyimpl = "pypy" if PYPY else "python" - if which('dpkg'): - missdeps( - "sudo apt-get install gcc %s%s-dev" % (pyimpl, py3) - ) - elif which('rpm'): - missdeps("sudo yum install gcc %s%s-devel" % (pyimpl, py3)) - elif which('apk'): + if shutil.which("dpkg"): + missdeps("sudo apt-get install gcc %s3-dev" % (pyimpl)) + elif shutil.which("rpm"): + missdeps("sudo yum install gcc %s-devel" % (pyimpl)) + elif shutil.which("apk"): missdeps( "sudo apk add gcc %s%s-dev musl-dev linux-headers" - % (pyimpl, py3) + % (pyimpl) ) elif MACOS: msg = ( @@ -584,14 +583,14 @@ def main(): ) print(hilite(msg, color="red"), file=sys.stderr) elif FREEBSD: - if which('pkg'): - missdeps("pkg install gcc python%s" % py3) - elif which('mport'): # MidnightBSD - missdeps("mport install gcc python%s" % py3) + if shutil.which("pkg"): + missdeps("pkg install gcc python3") + elif shutil.which("mport"): # MidnightBSD + missdeps("mport install gcc python3") elif OPENBSD: - missdeps("pkg_add -v gcc python%s" % py3) + missdeps("pkg_add -v gcc python3") elif NETBSD: - missdeps("pkgin install gcc python%s" % py3) + missdeps("pkgin install gcc python3") elif SUNOS: missdeps( "sudo ln -s /usr/bin/gcc /usr/local/bin/cc && "