diff --git a/.github/workflows/bsd.yml b/.github/workflows/bsd.yml index 8182177c0..5358412a3 100644 --- a/.github/workflows/bsd.yml +++ b/.github/workflows/bsd.yml @@ -1,67 +1,47 @@ -# # Execute tests on *BSD platforms. Does not produce wheels. -# # Useful URLs: -# # https://github.com/vmactions/freebsd-vm -# # https://github.com/vmactions/openbsd-vm -# # https://github.com/vmactions/netbsd-vm +# Execute tests on *BSD platforms. Does not produce wheels. +# Useful URLs: +# https://github.com/vmactions/freebsd-vm +# https://github.com/vmactions/openbsd-vm +# https://github.com/vmactions/netbsd-vm -# on: [push, pull_request] -# name: bsd-tests -# 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: -# freebsd: -# runs-on: ubuntu-22.04 -# steps: -# - uses: actions/checkout@v4 -# - name: Run tests -# uses: vmactions/freebsd-vm@v1 -# with: -# usesh: true -# prepare: | -# pkg install -y gcc python3 -# run: | -# set -e -x -# make install-pip -# python3 -m pip install --user setuptools -# make install -# make test -# make test-memleaks -# openbsd: -# runs-on: ubuntu-22.04 -# steps: -# - uses: actions/checkout@v4 -# - name: Run tests -# uses: vmactions/openbsd-vm@v1 -# with: -# usesh: true -# prepare: | -# set -e -# pkg_add gcc python3 -# run: | -# set -e -# make install-pip -# python3 -m pip install --user setuptools -# make install -# make test -# make test-memleaks -# netbsd: -# runs-on: ubuntu-22.04 -# steps: -# - uses: actions/checkout@v4 -# - name: Run tests -# uses: vmactions/netbsd-vm@v1 -# with: -# usesh: true -# prepare: | -# set -e -# /usr/sbin/pkg_add -v pkgin -# pkgin update -# pkgin -y install python311-* py311-setuptools-* gcc12-* -# run: | -# set -e -# make install-pip PYTHON=python3.11 -# python3.11 -m pip install --user setuptools -# make install PYTHON=python3.11 -# make test PYTHON=python3.11 -# make test-memleaks PYTHON=python3.11 +on: [push, pull_request] +name: bsd-tests +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: + freebsd: + # if: false + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Run tests + uses: vmactions/freebsd-vm@v1 + with: + usesh: true + run: | + PIP_BREAK_SYSTEM_PACKAGES=1 make install-sysdeps install-pydeps-test install print-sysinfo test test-memleaks + + openbsd: + # if: false + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Run tests + uses: vmactions/openbsd-vm@v1 + with: + usesh: true + run: | + PIP_BREAK_SYSTEM_PACKAGES=1 make install-sysdeps install-pydeps-test install print-sysinfo test test-memleaks + + netbsd: + # if: false + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Run tests + uses: vmactions/netbsd-vm@v1 + with: + usesh: true + run: | + PIP_BREAK_SYSTEM_PACKAGES=1 make PYTHON=python3.11 install-sysdeps install-pydeps-test install print-sysinfo test test-memleaks diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 29753ef82..a3290241e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,7 +18,7 @@ concurrency: jobs: # Linux + macOS + Windows Python 3 py3: - name: "py3-${{ matrix.os }}-${{ matrix.arch }}" + name: "py3, ${{ matrix.os }}, ${{ matrix.arch }}" runs-on: ${{ matrix.os }} timeout-minutes: 30 strategy: @@ -53,10 +53,13 @@ jobs: if: matrix.arch == 'aarch64' - name: Create wheels + run tests - uses: pypa/cibuildwheel@v2.19.2 + uses: pypa/cibuildwheel@v2.21.2 env: CIBW_ARCHS: "${{ matrix.arch }}" CIBW_PRERELEASE_PYTHONS: True + 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 - name: Upload wheels uses: actions/upload-artifact@v4 @@ -73,7 +76,7 @@ jobs: # Linux + macOS + Python 2 py2: - name: py2-${{ matrix.os }} + name: py2, ${{ matrix.os }} runs-on: ${{ matrix.os }} timeout-minutes: 20 strategy: @@ -81,11 +84,10 @@ jobs: matrix: os: [ubuntu-latest, macos-12] env: - CIBW_TEST_COMMAND: - PYTHONWARNINGS=always PYTHONUNBUFFERED=1 PSUTIL_DEBUG=1 python {project}/psutil/tests/runner.py && - PYTHONWARNINGS=always PYTHONUNBUFFERED=1 PSUTIL_DEBUG=1 python {project}/psutil/tests/test_memleaks.py - CIBW_TEST_EXTRAS: test 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 diff --git a/CREDITS b/CREDITS index 543536370..35f4b6b4a 100644 --- a/CREDITS +++ b/CREDITS @@ -835,3 +835,8 @@ I: 2272 N: Sam Gross W: https://github.com/colesbury I: 2401, 2427 + +N: Aleksey Lobanov +C: Russia +E: alex_github@likemath.ru +W: https://github.com/AlekseyLobanov diff --git a/HISTORY.rst b/HISTORY.rst index 2aa985bfe..e7687066d 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -1,14 +1,35 @@ *Bug tracker at https://github.com/giampaolo/psutil/issues* -6.0.1 (IN DEVELOPMENT) +6.1.0 (IN DEVELOPMENT) ====================== XXXX-XX-XX +**Enhancements** + +- 2446_: use pytest instead of unittest. +- 2448_: add ``make install-sysdeps`` target to install the necessary system + dependencies (python-dev, gcc, etc.) on all supported UNIX flavors. +- 2449_: add ``make install-pydeps-test`` and ``make install-pydeps-dev`` + targets. They can be used to install dependencies meant for running tests and + for local development. They can also be installed via ``pip install .[test]`` + and ``pip install .[dev]``. +- 2456_: allow to run tests via ``python3 -m psutil.tests`` even if ``pytest`` + module is not installed. This is useful for production environments that + don't have pytest installed, but still want to be able to test psutil + installation. + **Bug fixes** - 2427_: psutil (segfault) on import in the free-threaded (no GIL) version of Python 3.13. (patch by Sam Gross) +- 2455_, [Linux]: ``IndexError`` may occur when reading /proc/pid/stat and + field 40 (blkio_ticks) is missing. +- 2457_, [AIX]: significantly improve the speed of `Process.open_files()`_ for + some edge cases. +- 2460_, [OpenBSD]: `Process.num_fds()`_ and `Process.open_files()`_ may fail + with `NoSuchProcess`_ for PID 0. Instead, we now return "null" values (0 and + [] respectively). 6.0.0 ====== diff --git a/INSTALL.rst b/INSTALL.rst index e3dab9cb4..8e06d400e 100644 --- a/INSTALL.rst +++ b/INSTALL.rst @@ -12,10 +12,32 @@ install a C compiler. All you have to do is:: If wheels are not available for your platform or architecture, or you wish to build & install psutil from sources, keep reading. -Linux (build) -------------- +Compile psutil from sources +=========================== + +UNIX +---- + +On all UNIX systems you can use the `install-sysdeps.sh +`__ +script. This will install the system dependencies necessary to compile psutil +from sources. You can invoke this script from the Makefile as:: + + make install-sysdeps + +After system deps are installed, you can compile & install psutil with:: + + make build + make install -Ubuntu / Debian:: +...or this, which will fetch the latest source distribution from `PyPI `__:: + + pip install --no-binary :all: psutil + +Linux +----- + +Debian / Ubuntu:: sudo apt-get install gcc python3-dev pip install --no-binary :all: psutil @@ -30,14 +52,13 @@ Alpine:: sudo apk add gcc python3-dev musl-dev linux-headers pip install --no-binary :all: psutil -Windows (build) ---------------- +Windows +------- -In order to install psutil from sources on Windows you need Visual Studio -(MinGW is not supported). -Here's a couple of guides describing how to do it: `link `__ -and `link `__. -Once VS is installed do:: +In order to build / install psutil from sources on Windows you need to install +`Visua Studio 2017 `__ +or later (see cPython `devguide `__'s instructions). +MinGW is not supported. Once Visual Studio is installed do:: pip install --no-binary :all: psutil @@ -61,7 +82,7 @@ OpenBSD :: - export PKG_PATH=http://ftp.eu.openbsd.org/pub/OpenBSD/`uname -r`/packages/`uname -m`/ + export PKG_PATH=https://cdn.openbsd.org/pub/OpenBSD/`uname -r`/packages/`uname -m`/ pkg_add -v python3 gcc pip install psutil @@ -72,7 +93,7 @@ Assuming Python 3.11 (the most recent at the time of writing): :: - export PKG_PATH="http://ftp.netbsd.org/pub/pkgsrc/packages/NetBSD/`uname -m`/`uname -r`/All" + export PKG_PATH="https://ftp.netbsd.org/pub/pkgsrc/packages/NetBSD/`uname -m`/`uname -r`/All" pkg_add -v pkgin pkgin install python311-* gcc12-* py311-setuptools-* py311-pip-* python3.11 -m pip install psutil @@ -96,7 +117,7 @@ Install pip ----------- Pip is shipped by default with Python 2.7.9+ and 3.4+. -If you don't have pip you can install with wget:: +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 bb60aa849..91e5e2acc 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -153,7 +153,6 @@ include psutil/arch/windows/wmi.h include psutil/tests/README.rst include psutil/tests/__init__.py include psutil/tests/__main__.py -include psutil/tests/runner.py include psutil/tests/test_aix.py include psutil/tests/test_bsd.py include psutil/tests/test_connections.py @@ -188,6 +187,8 @@ include scripts/internal/download_wheels_appveyor.py include scripts/internal/download_wheels_github.py include scripts/internal/generate_manifest.py include scripts/internal/git_pre_commit.py +include scripts/internal/install-sysdeps.sh +include scripts/internal/install_pip.py include scripts/internal/print_access_denied.py include scripts/internal/print_announce.py include scripts/internal/print_api_speed.py diff --git a/Makefile b/Makefile index 473ba6d3a..7a38ee57f 100644 --- a/Makefile +++ b/Makefile @@ -2,56 +2,31 @@ # To use a specific Python version run: "make install PYTHON=python3.3" # You can set the variables below from the command line. -# Configurable. +# Configurable PYTHON = python3 -PYTHON_ENV_VARS = PYTHONWARNINGS=always PYTHONUNBUFFERED=1 PSUTIL_DEBUG=1 ARGS = -TSCRIPT = psutil/tests/runner.py - -# Internal. -PY3_DEPS = \ - black \ - check-manifest \ - concurrencytest \ - coverage \ - packaging \ - pylint \ - pyperf \ - pypinfo \ - requests \ - rstcheck \ - ruff \ - setuptools \ - sphinx_rtd_theme \ - teyit \ - toml-sort \ - twine \ - virtualenv \ - wheel -PY2_DEPS = \ - futures \ - ipaddress \ - mock -PY_DEPS = `$(PYTHON) -c \ - "import sys; \ - py3 = sys.version_info[0] == 3; \ - py38 = sys.version_info[:2] >= (3, 8); \ - py3_extra = ' abi3audit' if py38 else ''; \ - print('$(PY3_DEPS)' + py3_extra if py3 else '$(PY2_DEPS)')"` -NUM_WORKERS = `$(PYTHON) -c "import os; print(os.cpu_count() or 1)"` + # "python3 setup.py build" can be parallelized on Python >= 3.6. -BUILD_OPTS = `$(PYTHON) -c \ +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. -INSTALL_OPTS = `$(PYTHON) -c \ +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')"` +PIP_INSTALL_ARGS = --trusted-host files.pythonhosted.org --trusted-host pypi.org --upgrade +PYTEST_ARGS = -v -s --tb=short +PYTHON_ENV_VARS = PYTHONWARNINGS=always PYTHONUNBUFFERED=1 PSUTIL_DEBUG=1 + # if make is invoked with no arg, default to `make help` .DEFAULT_GOAL := help +# install git hook +_ := $(shell mkdir -p .git/hooks/ && ln -sf ../../scripts/internal/git_pre_commit.py .git/hooks/pre-commit && chmod +x .git/hooks/pre-commit) + # =================================================================== # Install # =================================================================== @@ -87,44 +62,37 @@ 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 $(BUILD_OPTS) + $(PYTHON_ENV_VARS) $(PYTHON) setup.py build_ext -i $(SETUP_BUILD_EXT_ARGS) $(PYTHON_ENV_VARS) $(PYTHON) -c "import psutil" # make sure it actually worked install: ## Install this package as current user in "edit" mode. ${MAKE} build - $(PYTHON_ENV_VARS) $(PYTHON) setup.py develop $(INSTALL_OPTS) - $(PYTHON_ENV_VARS) $(PYTHON) -c "import psutil" # make sure it actually worked + $(PYTHON_ENV_VARS) $(PYTHON) setup.py develop $(SETUP_INSTALL_ARGS) uninstall: ## Uninstall this package via pip. cd ..; $(PYTHON_ENV_VARS) $(PYTHON) -m pip uninstall -y -v psutil || true $(PYTHON_ENV_VARS) $(PYTHON) scripts/internal/purge_installation.py install-pip: ## Install pip (no-op if already installed). - @$(PYTHON) -c \ - "import sys, ssl, os, pkgutil, tempfile, atexit; \ - sys.exit(0) if pkgutil.find_loader('pip') else None; \ - PY3 = sys.version_info[0] == 3; \ - pyexc = 'from urllib.request import urlopen' if PY3 else 'from urllib2 import urlopen'; \ - exec(pyexc); \ - ctx = ssl._create_unverified_context() if hasattr(ssl, '_create_unverified_context') else None; \ - url = 'https://bootstrap.pypa.io/pip/2.7/get-pip.py' if not PY3 else 'https://bootstrap.pypa.io/get-pip.py'; \ - kw = dict(context=ctx) if ctx else {}; \ - req = urlopen(url, **kw); \ - data = req.read(); \ - f = tempfile.NamedTemporaryFile(suffix='.py'); \ - atexit.register(f.close); \ - f.write(data); \ - f.flush(); \ - print('downloaded %s' % f.name); \ - code = os.system('%s %s --user --upgrade' % (sys.executable, f.name)); \ - f.close(); \ - sys.exit(code);" - -setup-dev-env: ## Install GIT hooks, pip, test deps (also upgrades them). + $(PYTHON) scripts/internal/install_pip.py + +install-sysdeps: + ./scripts/internal/install-sysdeps.sh + +install-pydeps-test: ## Install python deps necessary to run unit tests. + ${MAKE} install-pip + $(PYTHON) -m pip install $(PIP_INSTALL_ARGS) pip # upgrade pip to latest version + $(PYTHON) -m pip install $(PIP_INSTALL_ARGS) `$(PYTHON) -c "import setup; print(' '.join(setup.TEST_DEPS))"` + +install-pydeps-dev: ## Install python deps meant for local development. ${MAKE} install-git-hooks ${MAKE} install-pip - $(PYTHON_ENV_VARS) $(PYTHON) -m pip install $(INSTALL_OPTS) --trusted-host files.pythonhosted.org --trusted-host pypi.org --upgrade pip - $(PYTHON_ENV_VARS) $(PYTHON) -m pip install $(INSTALL_OPTS) --trusted-host files.pythonhosted.org --trusted-host pypi.org --upgrade $(PY_DEPS) + $(PYTHON) -m pip install $(PIP_INSTALL_ARGS) pip # upgrade pip to latest version + $(PYTHON) -m pip install $(PIP_INSTALL_ARGS) `$(PYTHON) -c "import setup; print(' '.join(setup.TEST_DEPS + setup.DEV_DEPS))"` + +install-git-hooks: ## Install GIT pre-commit hook. + ln -sf ../../scripts/internal/git_pre_commit.py .git/hooks/pre-commit + chmod +x .git/hooks/pre-commit # =================================================================== # Tests @@ -132,65 +100,65 @@ setup-dev-env: ## Install GIT hooks, pip, test deps (also upgrades them). test: ## Run all tests. To run a specific test do "make test ARGS=psutil.tests.test_system.TestDiskAPIs" ${MAKE} build - $(PYTHON_ENV_VARS) $(PYTHON) $(TSCRIPT) $(ARGS) + $(PYTHON_ENV_VARS) $(PYTHON) -m pytest $(PYTEST_ARGS) --ignore=psutil/tests/test_memleaks.py $(ARGS) test-parallel: ## Run all tests in parallel. ${MAKE} build - $(PYTHON_ENV_VARS) $(PYTHON) $(TSCRIPT) $(ARGS) --parallel + $(PYTHON_ENV_VARS) $(PYTHON) -m pytest $(PYTEST_ARGS) --ignore=psutil/tests/test_memleaks.py -n auto --dist loadgroup $(ARGS) test-process: ## Run process-related API tests. ${MAKE} build - $(PYTHON_ENV_VARS) $(PYTHON) $(TSCRIPT) $(ARGS) psutil/tests/test_process.py + $(PYTHON_ENV_VARS) $(PYTHON) -m pytest $(PYTEST_ARGS) $(ARGS) psutil/tests/test_process.py test-process-all: ## Run tests which iterate over all process PIDs. ${MAKE} build - $(PYTHON_ENV_VARS) $(PYTHON) $(TSCRIPT) $(ARGS) psutil/tests/test_process_all.py + $(PYTHON_ENV_VARS) $(PYTHON) -m pytest $(PYTEST_ARGS) $(ARGS) psutil/tests/test_process_all.py test-system: ## Run system-related API tests. ${MAKE} build - $(PYTHON_ENV_VARS) $(PYTHON) $(TSCRIPT) $(ARGS) psutil/tests/test_system.py + $(PYTHON_ENV_VARS) $(PYTHON) -m pytest $(PYTEST_ARGS) $(ARGS) psutil/tests/test_system.py test-misc: ## Run miscellaneous tests. ${MAKE} build - $(PYTHON_ENV_VARS) $(PYTHON) $(TSCRIPT) $(ARGS) psutil/tests/test_misc.py + $(PYTHON_ENV_VARS) $(PYTHON) -m pytest $(PYTEST_ARGS) $(ARGS) psutil/tests/test_misc.py test-testutils: ## Run test utils tests. ${MAKE} build - $(PYTHON_ENV_VARS) $(PYTHON) $(TSCRIPT) $(ARGS) psutil/tests/test_testutils.py + $(PYTHON_ENV_VARS) $(PYTHON) -m pytest $(PYTEST_ARGS) $(ARGS) psutil/tests/test_testutils.py test-unicode: ## Test APIs dealing with strings. ${MAKE} build - $(PYTHON_ENV_VARS) $(PYTHON) $(TSCRIPT) $(ARGS) psutil/tests/test_unicode.py + $(PYTHON_ENV_VARS) $(PYTHON) -m pytest $(PYTEST_ARGS) $(ARGS) psutil/tests/test_unicode.py test-contracts: ## APIs sanity tests. ${MAKE} build - $(PYTHON_ENV_VARS) $(PYTHON) $(TSCRIPT) $(ARGS) psutil/tests/test_contracts.py + $(PYTHON_ENV_VARS) $(PYTHON) -m pytest $(PYTEST_ARGS) $(ARGS) psutil/tests/test_contracts.py test-connections: ## Test psutil.net_connections() and Process.net_connections(). ${MAKE} build - $(PYTHON_ENV_VARS) $(PYTHON) $(TSCRIPT) $(ARGS) psutil/tests/test_connections.py + $(PYTHON_ENV_VARS) $(PYTHON) -m pytest $(PYTEST_ARGS) $(ARGS) psutil/tests/test_connections.py test-posix: ## POSIX specific tests. ${MAKE} build - $(PYTHON_ENV_VARS) $(PYTHON) $(TSCRIPT) $(ARGS) psutil/tests/test_posix.py + $(PYTHON_ENV_VARS) $(PYTHON) -m pytest $(PYTEST_ARGS) $(ARGS) psutil/tests/test_posix.py test-platform: ## Run specific platform tests only. ${MAKE} build - $(PYTHON_ENV_VARS) $(PYTHON) $(TSCRIPT) $(ARGS) psutil/tests/test_`$(PYTHON) -c 'import psutil; print([x.lower() for x in ("LINUX", "BSD", "OSX", "SUNOS", "WINDOWS", "AIX") if getattr(psutil, x)][0])'`.py + $(PYTHON_ENV_VARS) $(PYTHON) -m pytest $(PYTEST_ARGS) $(ARGS) psutil/tests/test_`$(PYTHON) -c 'import psutil; print([x.lower() for x in ("LINUX", "BSD", "OSX", "SUNOS", "WINDOWS", "AIX") if getattr(psutil, x)][0])'`.py test-memleaks: ## Memory leak tests. ${MAKE} build - $(PYTHON_ENV_VARS) $(PYTHON) $(TSCRIPT) $(ARGS) psutil/tests/test_memleaks.py + $(PYTHON_ENV_VARS) $(PYTHON) -m pytest $(PYTEST_ARGS) $(ARGS) psutil/tests/test_memleaks.py test-last-failed: ## Re-run tests which failed on last run ${MAKE} build - $(PYTHON_ENV_VARS) $(PYTHON) $(TSCRIPT) $(ARGS) --last-failed + $(PYTHON_ENV_VARS) $(PYTHON) -m pytest $(PYTEST_ARGS) --last-failed $(ARGS) test-coverage: ## Run test coverage. ${MAKE} build # Note: coverage options are controlled by .coveragerc file rm -rf .coverage htmlcov - $(PYTHON_ENV_VARS) $(PYTHON) -m coverage run -m unittest -v + $(PYTHON_ENV_VARS) $(PYTHON) -m coverage run -m pytest $(PYTEST_ARGS) --ignore=psutil/tests/test_memleaks.py $(ARGS) $(PYTHON) -m coverage report @echo "writing results to htmlcov/index.html" $(PYTHON) -m coverage html @@ -201,13 +169,13 @@ test-coverage: ## Run test coverage. # =================================================================== ruff: ## Run ruff linter. - @git ls-files '*.py' | xargs $(PYTHON) -m ruff check --no-cache --output-format=concise + @git ls-files '*.py' | xargs $(PYTHON) -m ruff check --output-format=concise black: ## Python files linting (via black) @git ls-files '*.py' | xargs $(PYTHON) -m black --check --safe _pylint: ## Python pylint (not mandatory, just run it from time to time) - @git ls-files '*.py' | xargs $(PYTHON) -m pylint --rcfile=pyproject.toml --jobs=${NUM_WORKERS} + @git ls-files '*.py' | xargs $(PYTHON) -m pylint --rcfile=pyproject.toml --jobs=0 lint-c: ## Run C linter. @git ls-files '*.c' '*.h' | xargs $(PYTHON) scripts/internal/clinter.py @@ -233,10 +201,7 @@ fix-black: @git ls-files '*.py' | xargs $(PYTHON) -m black fix-ruff: - @git ls-files '*.py' | xargs $(PYTHON) -m ruff check --no-cache --fix $(ARGS) - -fix-unittests: ## Fix unittest idioms. - @git ls-files '*test_*.py' | xargs $(PYTHON) -m teyit --show-stats + @git ls-files '*.py' | xargs $(PYTHON) -m ruff check --fix --output-format=concise $(ARGS) fix-toml: ## Fix pyproject.toml @git ls-files '*.toml' | xargs toml-sort @@ -244,17 +209,8 @@ fix-toml: ## Fix pyproject.toml fix-all: ## Run all code fixers. ${MAKE} fix-ruff ${MAKE} fix-black - ${MAKE} fix-unittests ${MAKE} fix-toml -# =================================================================== -# GIT -# =================================================================== - -install-git-hooks: ## Install GIT pre-commit hook. - ln -sf ../../scripts/internal/git_pre_commit.py .git/hooks/pre-commit - chmod +x .git/hooks/pre-commit - # =================================================================== # Distribution # =================================================================== @@ -271,6 +227,10 @@ download-wheels-appveyor: ## Download latest wheels hosted on appveyor. $(PYTHON_ENV_VARS) $(PYTHON) scripts/internal/download_wheels_appveyor.py ${MAKE} print-dist +create-wheels: ## Create .whl files + $(PYTHON_ENV_VARS) $(PYTHON) setup.py bdist_wheel + ${MAKE} check-wheels + check-sdist: ## Check sanity of source distribution. $(PYTHON_ENV_VARS) $(PYTHON) -m virtualenv --clear --no-wheel --quiet build/venv $(PYTHON_ENV_VARS) build/venv/bin/python -m pip install -v --isolated --quiet dist/*.tar.gz @@ -286,20 +246,20 @@ pre-release: ## Check if we're ready to produce a new release. ${MAKE} sdist ${MAKE} check-sdist ${MAKE} install - $(PYTHON) -c \ + @$(PYTHON) -c \ "import requests, sys; \ from packaging.version import parse; \ from psutil import __version__; \ res = requests.get('https://pypi.org/pypi/psutil/json', timeout=5); \ versions = sorted(res.json()['releases'], key=parse, reverse=True); \ sys.exit('version %r already exists on PYPI' % __version__) if __version__ in versions else 0" - $(PYTHON) -c \ + @$(PYTHON) -c \ "from psutil import __version__ as ver; \ doc = open('docs/index.rst').read(); \ history = open('HISTORY.rst').read(); \ - assert ver in doc, '%r not in docs/index.rst' % ver; \ - assert ver in history, '%r not in HISTORY.rst' % ver; \ - assert 'XXXX' not in history, 'XXXX in HISTORY.rst';" + 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} check-wheels @@ -347,6 +307,9 @@ print-downloads: ## Print PYPI download statistics print-hashes: ## Prints hashes of files in dist/ directory $(PYTHON) scripts/internal/print_hashes.py dist/ +print-sysinfo: ## Prints system info + $(PYTHON) -c "from psutil.tests import print_sysinfo; print_sysinfo()" + # =================================================================== # Misc # =================================================================== diff --git a/appveyor.yml b/appveyor.yml index 70a4daec2..7488400d9 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -36,10 +36,10 @@ init: install: - "%WITH_COMPILER% %PYTHON%/python.exe -m pip --version" - - "%WITH_COMPILER% %PYTHON%/python.exe -m pip install --upgrade --user setuptools pip" - - "%WITH_COMPILER% %PYTHON%/python.exe scripts/internal/winmake.py setup-dev-env" + - "%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 diff --git a/docs/DEVGUIDE.rst b/docs/DEVGUIDE.rst index 744ddadf4..563c9b883 100644 --- a/docs/DEVGUIDE.rst +++ b/docs/DEVGUIDE.rst @@ -12,43 +12,56 @@ Once you have a compiler installed run: .. code-block:: bash git clone git@github.com:giampaolo/psutil.git - make setup-dev-env # install useful dev libs (ruff, coverage, ...) + make install-sysdeps # install gcc and python headers + make install-pydeps-test # install python deps necessary to run unit tests make build make install make test -- ``make`` (see `Makefile`_) is the designated tool to run tests, build, install - try new features you're developing, etc. This also includes Windows (see - `make.bat`_ ). - Some useful commands are: +- If you don't have the source code, and just want to test psutil installation. + This will work also if ``pytest`` module is not installed (e.g. production + environments) by using unittest's test runner: .. code-block:: bash - make test-parallel # faster - make test-memleaks - make test-coverage - make lint-all # Run Python and C linter - make fix-all # Fix linting errors + python3 -m psutil.tests + +- ``make`` (and the accompanying `Makefile`_) is the designated tool to build, + install, run tests and do pretty much anything that involves development. + This also includes Windows, meaning that you can run `make command` similarly + as if you were on UNIX (see `make.bat`_ and `winmake.py`_). Some useful + commands are: + +.. code-block:: bash + + make clean # remove build files + make install-pydeps-dev # install dev deps (ruff, black, ...) + make test # run tests + make test-parallel # run tests in parallel (faster) + make test-memleaks # run memory leak tests + make test-coverage # run test coverage + make lint-all # run linters + make fix-all # fix linters errors make uninstall make help - - To run a specific unit test: .. code-block:: bash - make test ARGS=psutil.tests.test_system.TestDiskAPIs + make test ARGS=psutil/tests/test_system.py::TestDiskAPIs::test_disk_usage - If you're working on a new feature and you wish to compile & test it "on the fly" from a test script, this is a quick & dirty way to do it: .. code-block:: bash - make test TSCRIPT=test_script.py # on UNIX + make test ARGS=test_script.py # on UNIX make test test_script.py # on Windows - Do not use ``sudo``. ``make install`` installs psutil as a limited user in - "edit" mode, meaning you can edit psutil code on the fly while you develop. + "edit" / development mode, meaning you can edit psutil code on the fly while + you develop. - If you want to target a specific Python version: @@ -60,10 +73,12 @@ Once you have a compiler installed run: Coding style ------------ -- Oython code strictly follows `PEP-8`_ styling guides and this is enforced by - a commit GIT hook installed via ``make install-git-hooks`` which will reject - commits if code is not PEP-8 complieant. -- C code should follow `PEP-7`_ styling guides. +- Python code strictly follows `PEP-8`_ styling guide. In addition we use + ``black`` and ``ruff`` code formatters / linters. This is enforced by a GIT + commit hook, installed via ``make install-git-hooks``, which will reject the + commit if code is not compliant. +- C code should follow `PEP-7`_ styling guides, but things are more relaxed. +- Linters are enforced also for ``.rst`` and ``.toml`` files. Code organization ----------------- @@ -101,7 +116,7 @@ Make a pull request - Fork psutil (go to https://github.com/giampaolo/psutil and click on "fork") - Git clone the fork locally: ``git clone git@github.com:YOUR-USERNAME/psutil.git`` -- Create a branch:``git checkout -b new-feature`` +- Create a branch: ``git checkout -b new-feature`` - Commit your changes: ``git commit -am 'add some feature'`` - Push the branch: ``git push origin new-feature`` - Create a new PR via the GitHub web interface and sign-off your work (see @@ -118,13 +133,14 @@ Documentation ------------- - doc source code is written in a single file: ``docs/index.rst``. -- doc can be built with ``make setup-dev-env; cd docs; make html``. +- doc can be built with ``make install-pydeps-dev; cd docs; make html``. - public doc is hosted at https://psutil.readthedocs.io. .. _`CREDITS`: https://github.com/giampaolo/psutil/blob/master/CREDITS .. _`CONTRIBUTING.md`: https://github.com/giampaolo/psutil/blob/master/CONTRIBUTING.md .. _`HISTORY.rst`: https://github.com/giampaolo/psutil/blob/master/HISTORY.rst .. _`make.bat`: https://github.com/giampaolo/psutil/blob/master/make.bat +.. _`winmake.py`: https://github.com/giampaolo/psutil/blob/master/scripts/internal/winmake.py .. _`Makefile`: https://github.com/giampaolo/psutil/blob/master/Makefile .. _`PEP-7`: https://www.python.org/dev/peps/pep-0007/ .. _`PEP-8`: https://www.python.org/dev/peps/pep-0008/ diff --git a/docs/Makefile b/docs/Makefile index 860a2b0e2..be7e058b0 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -229,6 +229,6 @@ dummy: @echo @echo "Build finished. Dummy builder generates no files." -.PHONY: setup-dev-env -setup-dev-env: ## Install GIT hooks, pip, test deps (also upgrades them). +.PHONY: install-pydeps +install-pydeps: ## Install GIT hooks, pip, test deps (also upgrades them). $(PYTHON) -m pip install --user --upgrade --trusted-host files.pythonhosted.org $(DEPS) diff --git a/make.bat b/make.bat index 2ac53d9ec..ec4fc591e 100644 --- a/make.bat +++ b/make.bat @@ -16,19 +16,12 @@ rem ...therefore it might not work on your Windows installation. rem rem To compile for a specific Python version run: rem set PYTHON=C:\Python34\python.exe & make.bat build -rem -rem To use a different test script: -rem set TSCRIPT=foo.py & make.bat test rem ========================================================================== if "%PYTHON%" == "" ( set PYTHON=python ) -if "%TSCRIPT%" == "" ( - set TSCRIPT=psutil\tests\runner.py -) - rem Needed to locate the .pypirc file and upload exes on PyPI. set HOME=%USERPROFILE% set PSUTIL_DEBUG=1 diff --git a/psutil/__init__.py b/psutil/__init__.py index d15425e9d..aabf71592 100644 --- a/psutil/__init__.py +++ b/psutil/__init__.py @@ -214,7 +214,7 @@ AF_LINK = _psplatform.AF_LINK __author__ = "Giampaolo Rodola'" -__version__ = "6.0.1" +__version__ = "6.1.0" version_info = tuple([int(num) for num in __version__.split('.')]) _timer = getattr(time, 'monotonic', time.time) diff --git a/psutil/_psaix.py b/psutil/_psaix.py index f48425eb8..2ccc638bc 100644 --- a/psutil/_psaix.py +++ b/psutil/_psaix.py @@ -349,7 +349,7 @@ def wrapper(self, *args, **kwargs): class Process: """Wrapper class around underlying C implementation.""" - __slots__ = ["pid", "_name", "_ppid", "_procfs_path", "_cache"] + __slots__ = ["_cache", "_name", "_ppid", "_procfs_path", "pid"] def __init__(self, pid): self.pid = pid @@ -539,7 +539,7 @@ def open_files(self): ) if "no such process" in stderr.lower(): raise NoSuchProcess(self.pid, self._name) - procfiles = re.findall(r"(\d+): S_IFREG.*\s*.*name:(.*)\n", stdout) + procfiles = re.findall(r"(\d+): S_IFREG.*name:(.*)\n", stdout) retlist = [] for fd, path in procfiles: path = path.strip() diff --git a/psutil/_psbsd.py b/psutil/_psbsd.py index cf84207e7..deffe50b0 100644 --- a/psutil/_psbsd.py +++ b/psutil/_psbsd.py @@ -640,7 +640,7 @@ def wrap_exceptions_procfs(inst): class Process: """Wrapper class around underlying C implementation.""" - __slots__ = ["pid", "_name", "_ppid", "_cache"] + __slots__ = ["_cache", "_name", "_ppid", "pid"] def __init__(self, pid): self.pid = pid diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py index 167183881..d7e76c4f5 100644 --- a/psutil/_pslinux.py +++ b/psutil/_pslinux.py @@ -1732,7 +1732,7 @@ def wrapper(self, *args, **kwargs): class Process: """Linux process implementation.""" - __slots__ = ["pid", "_name", "_ppid", "_procfs_path", "_cache"] + __slots__ = ["_cache", "_name", "_ppid", "_procfs_path", "pid"] def __init__(self, pid): self.pid = pid @@ -1796,7 +1796,12 @@ def _parse_stat_file(self): ret['children_stime'] = fields[14] ret['create_time'] = fields[19] ret['cpu_num'] = fields[36] - ret['blkio_ticks'] = fields[39] # aka 'delayacct_blkio_ticks' + try: + ret['blkio_ticks'] = fields[39] # aka 'delayacct_blkio_ticks' + except IndexError: + # https://github.com/giampaolo/psutil/issues/2455 + debug("can't get blkio_ticks, set iowait to 0") + ret['blkio_ticks'] = 0 return ret diff --git a/psutil/_psosx.py b/psutil/_psosx.py index 41263fd73..ed9b319de 100644 --- a/psutil/_psosx.py +++ b/psutil/_psosx.py @@ -361,7 +361,7 @@ def wrapper(self, *args, **kwargs): class Process: """Wrapper class around underlying C implementation.""" - __slots__ = ["pid", "_name", "_ppid", "_cache"] + __slots__ = ["_cache", "_name", "_ppid", "pid"] def __init__(self, pid): self.pid = pid diff --git a/psutil/_pssunos.py b/psutil/_pssunos.py index 1c0b96e9e..5536d3507 100644 --- a/psutil/_pssunos.py +++ b/psutil/_pssunos.py @@ -387,7 +387,7 @@ def wrapper(self, *args, **kwargs): class Process: """Wrapper class around underlying C implementation.""" - __slots__ = ["pid", "_name", "_ppid", "_procfs_path", "_cache"] + __slots__ = ["_cache", "_name", "_ppid", "_procfs_path", "pid"] def __init__(self, pid): self.pid = pid diff --git a/psutil/_psutil_common.c b/psutil/_psutil_common.c index 2f2edca26..d16816972 100644 --- a/psutil/_psutil_common.c +++ b/psutil/_psutil_common.c @@ -89,7 +89,7 @@ PyErr_SetFromWindowsErr(int winerr) { * message. */ PyObject * -PyErr_SetFromOSErrnoWithSyscall(const char *syscall) { +psutil_PyErr_SetFromOSErrnoWithSyscall(const char *syscall) { char fullmsg[1024]; #ifdef PSUTIL_WINDOWS diff --git a/psutil/_psutil_common.h b/psutil/_psutil_common.h index 20c03ebd7..2cdfa9d4d 100644 --- a/psutil/_psutil_common.h +++ b/psutil/_psutil_common.h @@ -97,7 +97,7 @@ static const int PSUTIL_CONN_NONE = 128; PyObject* AccessDenied(const char *msg); PyObject* NoSuchProcess(const char *msg); -PyObject* PyErr_SetFromOSErrnoWithSyscall(const char *syscall); +PyObject* psutil_PyErr_SetFromOSErrnoWithSyscall(const char *syscall); // ==================================================================== // --- Global utils diff --git a/psutil/_psutil_posix.c b/psutil/_psutil_posix.c index 8ced7beaa..5df15530f 100644 --- a/psutil/_psutil_posix.c +++ b/psutil/_psutil_posix.c @@ -148,7 +148,7 @@ psutil_pid_exists(pid_t pid) { void psutil_raise_for_pid(long pid, char *syscall) { if (errno != 0) - PyErr_SetFromOSErrnoWithSyscall(syscall); + psutil_PyErr_SetFromOSErrnoWithSyscall(syscall); else if (psutil_pid_exists(pid) == 0) NoSuchProcess(syscall); else @@ -470,14 +470,14 @@ psutil_net_if_flags(PyObject *self, PyObject *args) { sock = socket(AF_INET, SOCK_DGRAM, 0); if (sock == -1) { - PyErr_SetFromOSErrnoWithSyscall("socket(SOCK_DGRAM)"); + psutil_PyErr_SetFromOSErrnoWithSyscall("socket(SOCK_DGRAM)"); goto error; } PSUTIL_STRNCPY(ifr.ifr_name, nic_name, sizeof(ifr.ifr_name)); ret = ioctl(sock, SIOCGIFFLAGS, &ifr); if (ret == -1) { - PyErr_SetFromOSErrnoWithSyscall("ioctl(SIOCGIFFLAGS)"); + psutil_PyErr_SetFromOSErrnoWithSyscall("ioctl(SIOCGIFFLAGS)"); goto error; } diff --git a/psutil/_pswindows.py b/psutil/_pswindows.py index 9e7d7712c..e4550c391 100644 --- a/psutil/_pswindows.py +++ b/psutil/_pswindows.py @@ -762,7 +762,7 @@ def wrapper(self, *args, **kwargs): class Process: """Wrapper class around underlying C implementation.""" - __slots__ = ["pid", "_name", "_ppid", "_cache"] + __slots__ = ["_cache", "_name", "_ppid", "pid"] def __init__(self, pid): self.pid = pid diff --git a/psutil/arch/bsd/proc.c b/psutil/arch/bsd/proc.c index e64cf80dc..959b28ee0 100644 --- a/psutil/arch/bsd/proc.c +++ b/psutil/arch/bsd/proc.c @@ -380,7 +380,7 @@ psutil_proc_environ(PyObject *self, PyObject *args) { #endif default: sprintf(errbuf, "kvm_getenvv(pid=%ld)", pid); - PyErr_SetFromOSErrnoWithSyscall(errbuf); + psutil_PyErr_SetFromOSErrnoWithSyscall(errbuf); break; } goto error; @@ -442,8 +442,17 @@ psutil_proc_open_files(PyObject *self, PyObject *args) { errno = 0; freep = kinfo_getfile(pid, &cnt); + if (freep == NULL) { -#if !defined(PSUTIL_OPENBSD) +#if defined(PSUTIL_OPENBSD) + if ((pid == 0) && (errno == ESRCH)) { + psutil_debug( + "open_files() returned ESRCH for PID 0; forcing `return []`" + ); + PyErr_Clear(); + return py_retlist; + } +#else psutil_raise_for_pid(pid, "kinfo_getfile()"); #endif goto error; diff --git a/psutil/arch/freebsd/cpu.c b/psutil/arch/freebsd/cpu.c index 9fa1a7dbe..29a5ec575 100644 --- a/psutil/arch/freebsd/cpu.c +++ b/psutil/arch/freebsd/cpu.c @@ -42,7 +42,7 @@ psutil_per_cpu_times(PyObject *self, PyObject *args) { size = sizeof(maxcpus); if (sysctlbyname("kern.smp.maxcpus", &maxcpus, &size, NULL, 0) < 0) { Py_DECREF(py_retlist); - return PyErr_SetFromOSErrnoWithSyscall( + return psutil_PyErr_SetFromOSErrnoWithSyscall( "sysctlbyname('kern.smp.maxcpus')"); } long cpu_time[maxcpus][CPUSTATES]; @@ -52,14 +52,16 @@ psutil_per_cpu_times(PyObject *self, PyObject *args) { mib[1] = HW_NCPU; len = sizeof(ncpu); if (sysctl(mib, 2, &ncpu, &len, NULL, 0) == -1) { - PyErr_SetFromOSErrnoWithSyscall("sysctl(HW_NCPU)"); + psutil_PyErr_SetFromOSErrnoWithSyscall("sysctl(HW_NCPU)"); goto error; } // per-cpu info size = sizeof(cpu_time); if (sysctlbyname("kern.cp_times", &cpu_time, &size, NULL, 0) == -1) { - PyErr_SetFromOSErrnoWithSyscall("sysctlbyname('kern.smp.maxcpus')"); + psutil_PyErr_SetFromOSErrnoWithSyscall( + "sysctlbyname('kern.smp.maxcpus')" + ); goto error; } @@ -126,23 +128,23 @@ psutil_cpu_stats(PyObject *self, PyObject *args) { size_t size = sizeof(v_soft); if (sysctlbyname("vm.stats.sys.v_soft", &v_soft, &size, NULL, 0)) { - return PyErr_SetFromOSErrnoWithSyscall( + return psutil_PyErr_SetFromOSErrnoWithSyscall( "sysctlbyname('vm.stats.sys.v_soft')"); } if (sysctlbyname("vm.stats.sys.v_intr", &v_intr, &size, NULL, 0)) { - return PyErr_SetFromOSErrnoWithSyscall( + return psutil_PyErr_SetFromOSErrnoWithSyscall( "sysctlbyname('vm.stats.sys.v_intr')"); } if (sysctlbyname("vm.stats.sys.v_syscall", &v_syscall, &size, NULL, 0)) { - return PyErr_SetFromOSErrnoWithSyscall( + return psutil_PyErr_SetFromOSErrnoWithSyscall( "sysctlbyname('vm.stats.sys.v_syscall')"); } if (sysctlbyname("vm.stats.sys.v_trap", &v_trap, &size, NULL, 0)) { - return PyErr_SetFromOSErrnoWithSyscall( + return psutil_PyErr_SetFromOSErrnoWithSyscall( "sysctlbyname('vm.stats.sys.v_trap')"); } if (sysctlbyname("vm.stats.sys.v_swtch", &v_swtch, &size, NULL, 0)) { - return PyErr_SetFromOSErrnoWithSyscall( + return psutil_PyErr_SetFromOSErrnoWithSyscall( "sysctlbyname('vm.stats.sys.v_swtch')"); } diff --git a/psutil/arch/freebsd/mem.c b/psutil/arch/freebsd/mem.c index 3326f63a1..0482ef72d 100644 --- a/psutil/arch/freebsd/mem.c +++ b/psutil/arch/freebsd/mem.c @@ -39,36 +39,44 @@ psutil_virtual_mem(PyObject *self, PyObject *args) { size_t buffers_size = sizeof(buffers); if (sysctlbyname("hw.physmem", &total, &size, NULL, 0)) { - return PyErr_SetFromOSErrnoWithSyscall("sysctlbyname('hw.physmem')"); + return psutil_PyErr_SetFromOSErrnoWithSyscall( + "sysctlbyname('hw.physmem')" + ); } if (sysctlbyname("vm.stats.vm.v_active_count", &active, &size, NULL, 0)) { - return PyErr_SetFromOSErrnoWithSyscall( + return psutil_PyErr_SetFromOSErrnoWithSyscall( "sysctlbyname('vm.stats.vm.v_active_count')"); } if (sysctlbyname("vm.stats.vm.v_inactive_count", &inactive, &size, NULL, 0)) { - return PyErr_SetFromOSErrnoWithSyscall( + return psutil_PyErr_SetFromOSErrnoWithSyscall( "sysctlbyname('vm.stats.vm.v_inactive_count')"); } if (sysctlbyname("vm.stats.vm.v_wire_count", &wired, &size, NULL, 0)) { - return PyErr_SetFromOSErrnoWithSyscall( - "sysctlbyname('vm.stats.vm.v_wire_count')"); + return psutil_PyErr_SetFromOSErrnoWithSyscall( + "sysctlbyname('vm.stats.vm.v_wire_count')" + ); } // https://github.com/giampaolo/psutil/issues/997 if (sysctlbyname("vm.stats.vm.v_cache_count", &cached, &size, NULL, 0)) { cached = 0; } if (sysctlbyname("vm.stats.vm.v_free_count", &free, &size, NULL, 0)) { - return PyErr_SetFromOSErrnoWithSyscall( - "sysctlbyname('vm.stats.vm.v_free_count')"); + return psutil_PyErr_SetFromOSErrnoWithSyscall( + "sysctlbyname('vm.stats.vm.v_free_count')" + ); } if (sysctlbyname("vfs.bufspace", &buffers, &buffers_size, NULL, 0)) { - return PyErr_SetFromOSErrnoWithSyscall("sysctlbyname('vfs.bufspace')"); + return psutil_PyErr_SetFromOSErrnoWithSyscall( + "sysctlbyname('vfs.bufspace')" + ); } size = sizeof(vm); if (sysctl(mib, 2, &vm, &size, NULL, 0) != 0) { - return PyErr_SetFromOSErrnoWithSyscall("sysctl(CTL_VM | VM_METER)"); + return psutil_PyErr_SetFromOSErrnoWithSyscall( + "sysctl(CTL_VM | VM_METER)" + ); } return Py_BuildValue("KKKKKKKK", @@ -109,20 +117,24 @@ psutil_swap_mem(PyObject *self, PyObject *args) { kvm_close(kd); if (sysctlbyname("vm.stats.vm.v_swapin", &swapin, &size, NULL, 0) == -1) { - return PyErr_SetFromOSErrnoWithSyscall( - "sysctlbyname('vm.stats.vm.v_swapin)'"); + return psutil_PyErr_SetFromOSErrnoWithSyscall( + "sysctlbyname('vm.stats.vm.v_swapin)'" + ); } if (sysctlbyname("vm.stats.vm.v_swapout", &swapout, &size, NULL, 0) == -1){ - return PyErr_SetFromOSErrnoWithSyscall( - "sysctlbyname('vm.stats.vm.v_swapout)'"); + return psutil_PyErr_SetFromOSErrnoWithSyscall( + "sysctlbyname('vm.stats.vm.v_swapout)'" + ); } if (sysctlbyname("vm.stats.vm.v_vnodein", &nodein, &size, NULL, 0) == -1) { - return PyErr_SetFromOSErrnoWithSyscall( - "sysctlbyname('vm.stats.vm.v_vnodein)'"); + return psutil_PyErr_SetFromOSErrnoWithSyscall( + "sysctlbyname('vm.stats.vm.v_vnodein)'" + ); } if (sysctlbyname("vm.stats.vm.v_vnodeout", &nodeout, &size, NULL, 0) == -1) { - return PyErr_SetFromOSErrnoWithSyscall( - "sysctlbyname('vm.stats.vm.v_vnodeout)'"); + return psutil_PyErr_SetFromOSErrnoWithSyscall( + "sysctlbyname('vm.stats.vm.v_vnodeout)'" + ); } return Py_BuildValue( @@ -135,4 +147,3 @@ psutil_swap_mem(PyObject *self, PyObject *args) { nodein + nodeout // swap out ); } - diff --git a/psutil/arch/freebsd/proc.c b/psutil/arch/freebsd/proc.c index 6528ece45..a81128b51 100644 --- a/psutil/arch/freebsd/proc.c +++ b/psutil/arch/freebsd/proc.c @@ -45,7 +45,7 @@ psutil_kinfo_proc(pid_t pid, struct kinfo_proc *proc) { size = sizeof(struct kinfo_proc); if (sysctl((int *)mib, 4, proc, &size, NULL, 0) == -1) { - PyErr_SetFromOSErrnoWithSyscall("sysctl(KERN_PROC_PID)"); + psutil_PyErr_SetFromOSErrnoWithSyscall("sysctl(KERN_PROC_PID)"); return -1; } @@ -92,7 +92,7 @@ psutil_get_proc_list(struct kinfo_proc **procList, size_t *procCount) { // Call sysctl with a NULL buffer in order to get buffer length. err = sysctl(name, 3, NULL, &length, NULL, 0); if (err == -1) { - PyErr_SetFromOSErrnoWithSyscall("sysctl (null buffer)"); + psutil_PyErr_SetFromOSErrnoWithSyscall("sysctl (null buffer)"); return 1; } @@ -120,7 +120,7 @@ psutil_get_proc_list(struct kinfo_proc **procList, size_t *procCount) { } } - PyErr_SetFromOSErrnoWithSyscall("sysctl()"); + psutil_PyErr_SetFromOSErrnoWithSyscall("sysctl()"); return 1; } else { @@ -177,7 +177,7 @@ psutil_proc_cmdline(PyObject *self, PyObject *args) { size = argmax; if (sysctl(mib, 4, procargs, &size, NULL, 0) == -1) { - PyErr_SetFromOSErrnoWithSyscall("sysctl(KERN_PROC_ARGS)"); + psutil_PyErr_SetFromOSErrnoWithSyscall("sysctl(KERN_PROC_ARGS)"); goto error; } @@ -238,8 +238,9 @@ psutil_proc_exe(PyObject *self, PyObject *args) { return PyUnicode_DecodeFSDefault(""); } else { - return \ - PyErr_SetFromOSErrnoWithSyscall("sysctl(KERN_PROC_PATHNAME)"); + return psutil_PyErr_SetFromOSErrnoWithSyscall( + "sysctl(KERN_PROC_PATHNAME)" + ); } } if (size == 0 || strlen(pathname) == 0) { @@ -300,7 +301,7 @@ psutil_proc_threads(PyObject *self, PyObject *args) { size = 0; error = sysctl(mib, 4, NULL, &size, NULL, 0); if (error == -1) { - PyErr_SetFromOSErrnoWithSyscall("sysctl(KERN_PROC_INC_THREAD)"); + psutil_PyErr_SetFromOSErrnoWithSyscall("sysctl(KERN_PROC_INC_THREAD)"); goto error; } if (size == 0) { @@ -316,7 +317,7 @@ psutil_proc_threads(PyObject *self, PyObject *args) { error = sysctl(mib, 4, kip, &size, NULL, 0); if (error == -1) { - PyErr_SetFromOSErrnoWithSyscall("sysctl(KERN_PROC_INC_THREAD)"); + psutil_PyErr_SetFromOSErrnoWithSyscall("sysctl(KERN_PROC_INC_THREAD)"); goto error; } if (size == 0) { diff --git a/psutil/arch/linux/net.c b/psutil/arch/linux/net.c index d193e9408..6d2785ee6 100644 --- a/psutil/arch/linux/net.c +++ b/psutil/arch/linux/net.c @@ -71,7 +71,7 @@ psutil_net_if_duplex_speed(PyObject* self, PyObject* args) { sock = socket(AF_INET, SOCK_DGRAM, 0); if (sock == -1) - return PyErr_SetFromOSErrnoWithSyscall("socket()"); + return psutil_PyErr_SetFromOSErrnoWithSyscall("socket()"); PSUTIL_STRNCPY(ifr.ifr_name, nic_name, sizeof(ifr.ifr_name)); // duplex and speed @@ -102,7 +102,7 @@ psutil_net_if_duplex_speed(PyObject* self, PyObject* args) { speed = 0; } else { - PyErr_SetFromOSErrnoWithSyscall("ioctl(SIOCETHTOOL)"); + psutil_PyErr_SetFromOSErrnoWithSyscall("ioctl(SIOCETHTOOL)"); goto error; } } diff --git a/psutil/arch/netbsd/proc.c b/psutil/arch/netbsd/proc.c index 4cd43c4c7..7613b5459 100644 --- a/psutil/arch/netbsd/proc.c +++ b/psutil/arch/netbsd/proc.c @@ -352,7 +352,9 @@ psutil_proc_cmdline(PyObject *self, PyObject *args) { st = sysctl(mib, __arraycount(mib), NULL, &len, NULL, 0); if (st == -1) { - PyErr_SetFromOSErrnoWithSyscall("sysctl(KERN_PROC_ARGV) get size"); + psutil_PyErr_SetFromOSErrnoWithSyscall( + "sysctl(KERN_PROC_ARGV) get size" + ); goto error; } @@ -382,7 +384,7 @@ psutil_proc_cmdline(PyObject *self, PyObject *args) { } } else { - PyErr_SetFromOSErrnoWithSyscall("sysctl(KERN_PROC_ARGV)"); + psutil_PyErr_SetFromOSErrnoWithSyscall("sysctl(KERN_PROC_ARGV)"); goto error; } } diff --git a/psutil/arch/openbsd/proc.c b/psutil/arch/openbsd/proc.c index 0881ccd55..bf4015be4 100644 --- a/psutil/arch/openbsd/proc.c +++ b/psutil/arch/openbsd/proc.c @@ -41,7 +41,7 @@ psutil_kinfo_proc(pid_t pid, struct kinfo_proc *proc) { ret = sysctl((int*)mib, 6, proc, &size, NULL, 0); if (ret == -1) { - PyErr_SetFromErrno(PyExc_OSError); + psutil_PyErr_SetFromOSErrnoWithSyscall("sysctl(kinfo_proc)"); return -1; } // sysctl stores 0 in the size if we can't find the process information. @@ -69,7 +69,7 @@ kinfo_getfile(pid_t pid, int* cnt) { /* get the size of what would be returned */ if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) { - PyErr_SetFromErrno(PyExc_OSError); + psutil_PyErr_SetFromOSErrnoWithSyscall("sysctl(kinfo_file) (1/2)"); return NULL; } if ((kf = malloc(len)) == NULL) { @@ -79,7 +79,7 @@ kinfo_getfile(pid_t pid, int* cnt) { mib[5] = (int)(len / sizeof(struct kinfo_file)); if (sysctl(mib, 6, kf, &len, NULL, 0) < 0) { free(kf); - PyErr_SetFromErrno(PyExc_OSError); + psutil_PyErr_SetFromOSErrnoWithSyscall("sysctl(kinfo_file) (2/2)"); return NULL; } @@ -288,8 +288,19 @@ psutil_proc_num_fds(PyObject *self, PyObject *args) { return NULL; freep = kinfo_getfile(pid, &cnt); - if (freep == NULL) + + if (freep == NULL) { +#if defined(PSUTIL_OPENBSD) + if ((pid == 0) && (errno == ESRCH)) { + psutil_debug( + "num_fds() returned ESRCH for PID 0; forcing `return 0`" + ); + PyErr_Clear(); + return Py_BuildValue("i", 0); + } +#endif return NULL; + } free(freep); return Py_BuildValue("i", cnt); diff --git a/psutil/arch/openbsd/socks.c b/psutil/arch/openbsd/socks.c index 69daa447b..05849aa1d 100644 --- a/psutil/arch/openbsd/socks.c +++ b/psutil/arch/openbsd/socks.c @@ -67,7 +67,7 @@ psutil_net_connections(PyObject *self, PyObject *args) { ikf = kvm_getfiles(kd, KERN_FILE_BYPID, -1, sizeof(*ikf), &cnt); if (! ikf) { - PyErr_SetFromOSErrnoWithSyscall("kvm_getfiles"); + psutil_PyErr_SetFromOSErrnoWithSyscall("kvm_getfiles"); goto error; } diff --git a/psutil/arch/osx/cpu.c b/psutil/arch/osx/cpu.c index 4196083ec..d2f8b1855 100644 --- a/psutil/arch/osx/cpu.c +++ b/psutil/arch/osx/cpu.c @@ -243,7 +243,7 @@ psutil_cpu_freq(PyObject *self, PyObject *args) { mib[1] = HW_CPU_FREQ; if (sysctl(mib, 2, &curr, &len, NULL, 0) < 0) - return PyErr_SetFromOSErrnoWithSyscall("sysctl(HW_CPU_FREQ)"); + return psutil_PyErr_SetFromOSErrnoWithSyscall("sysctl(HW_CPU_FREQ)"); if (sysctlbyname("hw.cpufrequency_min", &min, &size, NULL, 0)) psutil_debug("sysctl('hw.cpufrequency_min') failed (set to 0)"); diff --git a/psutil/arch/osx/proc.c b/psutil/arch/osx/proc.c index 2cdb9911c..136311ece 100644 --- a/psutil/arch/osx/proc.c +++ b/psutil/arch/osx/proc.c @@ -77,7 +77,7 @@ psutil_get_proc_list(kinfo_proc **procList, size_t *procCount) { while (lim-- > 0) { size = 0; if (sysctl((int *)mib, 3, NULL, &size, NULL, 0) == -1) { - PyErr_SetFromOSErrnoWithSyscall("sysctl(KERN_PROC_ALL)"); + psutil_PyErr_SetFromOSErrnoWithSyscall("sysctl(KERN_PROC_ALL)"); return 1; } size2 = size + (size >> 3); // add some @@ -100,7 +100,7 @@ psutil_get_proc_list(kinfo_proc **procList, size_t *procCount) { err = errno; free(ptr); if (err != ENOMEM) { - PyErr_SetFromOSErrnoWithSyscall("sysctl(KERN_PROC_ALL)"); + psutil_PyErr_SetFromOSErrnoWithSyscall("sysctl(KERN_PROC_ALL)"); return 1; } } @@ -132,7 +132,7 @@ psutil_sysctl_argmax() { if (sysctl(mib, 2, &argmax, &size, NULL, 0) == 0) return argmax; - PyErr_SetFromOSErrnoWithSyscall("sysctl(KERN_ARGMAX)"); + psutil_PyErr_SetFromOSErrnoWithSyscall("sysctl(KERN_ARGMAX)"); return 0; } @@ -164,7 +164,7 @@ psutil_sysctl_procargs(pid_t pid, char *procargs, size_t *argmax) { AccessDenied("sysctl(KERN_PROCARGS2) -> EIO"); return 1; } - PyErr_SetFromOSErrnoWithSyscall("sysctl(KERN_PROCARGS2)"); + psutil_PyErr_SetFromOSErrnoWithSyscall("sysctl(KERN_PROCARGS2)"); return 1; } return 0; @@ -186,7 +186,7 @@ psutil_get_kinfo_proc(pid_t pid, struct kinfo_proc *kp) { // now read the data from sysctl if (sysctl(mib, 4, kp, &len, NULL, 0) == -1) { // raise an exception and throw errno as the error - PyErr_SetFromOSErrnoWithSyscall("sysctl"); + psutil_PyErr_SetFromOSErrnoWithSyscall("sysctl"); return -1; } @@ -605,8 +605,9 @@ psutil_proc_memory_uss(PyObject *self, PyObject *args) { len = sizeof(cpu_type); if (sysctlbyname("sysctl.proc_cputype", &cpu_type, &len, NULL, 0) != 0) { - return PyErr_SetFromOSErrnoWithSyscall( - "sysctlbyname('sysctl.proc_cputype')"); + return psutil_PyErr_SetFromOSErrnoWithSyscall( + "sysctlbyname('sysctl.proc_cputype')" + ); } // Roughly based on libtop_update_vm_regions in @@ -977,7 +978,7 @@ psutil_proc_net_connections(PyObject *self, PyObject *args) { // check for inet_ntop failures if (errno != 0) { - PyErr_SetFromOSErrnoWithSyscall("inet_ntop()"); + psutil_PyErr_SetFromOSErrnoWithSyscall("inet_ntop()"); goto error; } diff --git a/psutil/arch/windows/proc.c b/psutil/arch/windows/proc.c index af3df267a..05fb50255 100644 --- a/psutil/arch/windows/proc.c +++ b/psutil/arch/windows/proc.c @@ -116,7 +116,7 @@ psutil_proc_kill(PyObject *self, PyObject *args) { // https://github.com/giampaolo/psutil/issues/1099 // http://bugs.python.org/issue14252 if (GetLastError() != ERROR_ACCESS_DENIED) { - PyErr_SetFromOSErrnoWithSyscall("TerminateProcess"); + psutil_PyErr_SetFromOSErrnoWithSyscall("TerminateProcess"); return NULL; } } @@ -151,7 +151,7 @@ psutil_proc_wait(PyObject *self, PyObject *args) { Py_RETURN_NONE; } else { - PyErr_SetFromOSErrnoWithSyscall("OpenProcess"); + psutil_PyErr_SetFromOSErrnoWithSyscall("OpenProcess"); return NULL; } } @@ -163,7 +163,7 @@ psutil_proc_wait(PyObject *self, PyObject *args) { // handle return code if (retVal == WAIT_FAILED) { - PyErr_SetFromOSErrnoWithSyscall("WaitForSingleObject"); + psutil_PyErr_SetFromOSErrnoWithSyscall("WaitForSingleObject"); CloseHandle(hProcess); return NULL; } @@ -185,7 +185,7 @@ psutil_proc_wait(PyObject *self, PyObject *args) { // process is gone so we can get its process exit code. The PID // may still stick around though but we'll handle that from Python. if (GetExitCodeProcess(hProcess, &ExitCode) == 0) { - PyErr_SetFromOSErrnoWithSyscall("GetExitCodeProcess"); + psutil_PyErr_SetFromOSErrnoWithSyscall("GetExitCodeProcess"); CloseHandle(hProcess); return NULL; } @@ -598,7 +598,7 @@ psutil_proc_threads(PyObject *self, PyObject *args) { hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); if (hThreadSnap == INVALID_HANDLE_VALUE) { - PyErr_SetFromOSErrnoWithSyscall("CreateToolhelp32Snapshot"); + psutil_PyErr_SetFromOSErrnoWithSyscall("CreateToolhelp32Snapshot"); goto error; } @@ -606,7 +606,7 @@ psutil_proc_threads(PyObject *self, PyObject *args) { te32.dwSize = sizeof(THREADENTRY32); if (! Thread32First(hThreadSnap, &te32)) { - PyErr_SetFromOSErrnoWithSyscall("Thread32First"); + psutil_PyErr_SetFromOSErrnoWithSyscall("Thread32First"); goto error; } @@ -626,7 +626,7 @@ psutil_proc_threads(PyObject *self, PyObject *args) { rc = GetThreadTimes(hThread, &ftDummy, &ftDummy, &ftKernel, &ftUser); if (rc == 0) { - PyErr_SetFromOSErrnoWithSyscall("GetThreadTimes"); + psutil_PyErr_SetFromOSErrnoWithSyscall("GetThreadTimes"); goto error; } @@ -702,7 +702,7 @@ _psutil_user_token_from_pid(DWORD pid) { return NULL; if (!OpenProcessToken(hProcess, TOKEN_QUERY, &hToken)) { - PyErr_SetFromOSErrnoWithSyscall("OpenProcessToken"); + psutil_PyErr_SetFromOSErrnoWithSyscall("OpenProcessToken"); goto error; } @@ -721,7 +721,7 @@ _psutil_user_token_from_pid(DWORD pid) { continue; } else { - PyErr_SetFromOSErrnoWithSyscall("GetTokenInformation"); + psutil_PyErr_SetFromOSErrnoWithSyscall("GetTokenInformation"); goto error; } } @@ -797,7 +797,7 @@ psutil_proc_username(PyObject *self, PyObject *args) { goto error; } else { - PyErr_SetFromOSErrnoWithSyscall("LookupAccountSidW"); + psutil_PyErr_SetFromOSErrnoWithSyscall("LookupAccountSidW"); goto error; } } diff --git a/psutil/arch/windows/proc_handles.c b/psutil/arch/windows/proc_handles.c index 30e7cd2d8..01ef6a425 100644 --- a/psutil/arch/windows/proc_handles.c +++ b/psutil/arch/windows/proc_handles.c @@ -156,7 +156,7 @@ psutil_threaded_get_filename(HANDLE hFile) { hThread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)psutil_get_filename, &hFile, 0, NULL); if (hThread == NULL) { - PyErr_SetFromOSErrnoWithSyscall("CreateThread"); + psutil_PyErr_SetFromOSErrnoWithSyscall("CreateThread"); return 1; } @@ -168,7 +168,7 @@ psutil_threaded_get_filename(HANDLE hFile) { psutil_debug( "get handle name thread timed out after %i ms", THREAD_TIMEOUT); if (TerminateThread(hThread, 0) == 0) { - PyErr_SetFromOSErrnoWithSyscall("TerminateThread"); + psutil_PyErr_SetFromOSErrnoWithSyscall("TerminateThread"); CloseHandle(hThread); return 1; } @@ -179,26 +179,28 @@ psutil_threaded_get_filename(HANDLE hFile) { if (dwWait == WAIT_FAILED) { psutil_debug("WaitForSingleObject -> WAIT_FAILED"); if (TerminateThread(hThread, 0) == 0) { - PyErr_SetFromOSErrnoWithSyscall( - "WaitForSingleObject -> WAIT_FAILED -> TerminateThread"); + psutil_PyErr_SetFromOSErrnoWithSyscall( + "WaitForSingleObject -> WAIT_FAILED -> TerminateThread" + ); CloseHandle(hThread); return 1; } - PyErr_SetFromOSErrnoWithSyscall("WaitForSingleObject"); + psutil_PyErr_SetFromOSErrnoWithSyscall("WaitForSingleObject"); CloseHandle(hThread); return 1; } if (GetExitCodeThread(hThread, &threadRetValue) == 0) { if (TerminateThread(hThread, 0) == 0) { - PyErr_SetFromOSErrnoWithSyscall( - "GetExitCodeThread (failed) -> TerminateThread"); + psutil_PyErr_SetFromOSErrnoWithSyscall( + "GetExitCodeThread (failed) -> TerminateThread" + ); CloseHandle(hThread); return 1; } CloseHandle(hThread); - PyErr_SetFromOSErrnoWithSyscall("GetExitCodeThread"); + psutil_PyErr_SetFromOSErrnoWithSyscall("GetExitCodeThread"); return 1; } CloseHandle(hThread); diff --git a/psutil/arch/windows/proc_info.c b/psutil/arch/windows/proc_info.c index 5d16b8133..9e0caf344 100644 --- a/psutil/arch/windows/proc_info.c +++ b/psutil/arch/windows/proc_info.c @@ -41,7 +41,7 @@ psutil_get_process_region_size(HANDLE hProcess, LPCVOID src, SIZE_T *psize) { MEMORY_BASIC_INFORMATION info; if (!VirtualQueryEx(hProcess, src, &info, sizeof(info))) { - PyErr_SetFromOSErrnoWithSyscall("VirtualQueryEx"); + psutil_PyErr_SetFromOSErrnoWithSyscall("VirtualQueryEx"); return -1; } @@ -67,7 +67,7 @@ psutil_convert_winerr(ULONG err, char* syscall) { AccessDenied(fullmsg); } else { - PyErr_SetFromOSErrnoWithSyscall(syscall); + psutil_PyErr_SetFromOSErrnoWithSyscall(syscall); } } @@ -226,7 +226,7 @@ psutil_get_process_data(DWORD pid, // 32 bit case. Check if the target is also 32 bit. if (!IsWow64Process(GetCurrentProcess(), &weAreWow64) || !IsWow64Process(hProcess, &theyAreWow64)) { - PyErr_SetFromOSErrnoWithSyscall("IsWow64Process"); + psutil_PyErr_SetFromOSErrnoWithSyscall("IsWow64Process"); goto error; } @@ -594,7 +594,7 @@ psutil_proc_cmdline(PyObject *self, PyObject *args, PyObject *kwdict) { // attempt to parse the command line using Win32 API szArglist = CommandLineToArgvW(data, &nArgs); if (szArglist == NULL) { - PyErr_SetFromOSErrnoWithSyscall("CommandLineToArgvW"); + psutil_PyErr_SetFromOSErrnoWithSyscall("CommandLineToArgvW"); goto error; } diff --git a/psutil/arch/windows/proc_utils.c b/psutil/arch/windows/proc_utils.c index 77b6dbf1e..1ebb76c44 100644 --- a/psutil/arch/windows/proc_utils.c +++ b/psutil/arch/windows/proc_utils.c @@ -103,7 +103,7 @@ psutil_check_phandle(HANDLE hProcess, DWORD pid, int check_exit_code) { } return NULL; } - PyErr_SetFromOSErrnoWithSyscall("OpenProcess"); + psutil_PyErr_SetFromOSErrnoWithSyscall("OpenProcess"); return NULL; } @@ -129,7 +129,7 @@ psutil_check_phandle(HANDLE hProcess, DWORD pid, int check_exit_code) { SetLastError(0); return hProcess; } - PyErr_SetFromOSErrnoWithSyscall("GetExitCodeProcess"); + psutil_PyErr_SetFromOSErrnoWithSyscall("GetExitCodeProcess"); CloseHandle(hProcess); return NULL; } @@ -151,7 +151,7 @@ psutil_handle_from_pid(DWORD pid, DWORD access) { hProcess = OpenProcess(access, FALSE, pid); if ((hProcess == NULL) && (GetLastError() == ERROR_ACCESS_DENIED)) { - PyErr_SetFromOSErrnoWithSyscall("OpenProcess"); + psutil_PyErr_SetFromOSErrnoWithSyscall("OpenProcess"); return NULL; } diff --git a/psutil/arch/windows/security.c b/psutil/arch/windows/security.c index 7e400a254..07d239984 100644 --- a/psutil/arch/windows/security.c +++ b/psutil/arch/windows/security.c @@ -21,7 +21,7 @@ psutil_set_privilege(HANDLE hToken, LPCTSTR Privilege, BOOL bEnablePrivilege) { DWORD cbPrevious = sizeof(TOKEN_PRIVILEGES); if (! LookupPrivilegeValue(NULL, Privilege, &luid)) { - PyErr_SetFromOSErrnoWithSyscall("LookupPrivilegeValue"); + psutil_PyErr_SetFromOSErrnoWithSyscall("LookupPrivilegeValue"); return 1; } @@ -38,7 +38,7 @@ psutil_set_privilege(HANDLE hToken, LPCTSTR Privilege, BOOL bEnablePrivilege) { &tpPrevious, &cbPrevious)) { - PyErr_SetFromOSErrnoWithSyscall("AdjustTokenPrivileges"); + psutil_PyErr_SetFromOSErrnoWithSyscall("AdjustTokenPrivileges"); return 1; } @@ -60,7 +60,7 @@ psutil_set_privilege(HANDLE hToken, LPCTSTR Privilege, BOOL bEnablePrivilege) { NULL, NULL)) { - PyErr_SetFromOSErrnoWithSyscall("AdjustTokenPrivileges"); + psutil_PyErr_SetFromOSErrnoWithSyscall("AdjustTokenPrivileges"); return 1; } @@ -79,18 +79,18 @@ psutil_get_thisproc_token() { if (GetLastError() == ERROR_NO_TOKEN) { if (! ImpersonateSelf(SecurityImpersonation)) { - PyErr_SetFromOSErrnoWithSyscall("ImpersonateSelf"); + psutil_PyErr_SetFromOSErrnoWithSyscall("ImpersonateSelf"); return NULL; } if (! OpenProcessToken( me, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) { - PyErr_SetFromOSErrnoWithSyscall("OpenProcessToken"); + psutil_PyErr_SetFromOSErrnoWithSyscall("OpenProcessToken"); return NULL; } } else { - PyErr_SetFromOSErrnoWithSyscall("OpenProcessToken"); + psutil_PyErr_SetFromOSErrnoWithSyscall("OpenProcessToken"); return NULL; } } diff --git a/psutil/arch/windows/services.c b/psutil/arch/windows/services.c index fa3e646e5..4931e9d66 100644 --- a/psutil/arch/windows/services.c +++ b/psutil/arch/windows/services.c @@ -24,12 +24,12 @@ psutil_get_service_handler(char *service_name, DWORD scm_access, DWORD access) sc = OpenSCManager(NULL, NULL, scm_access); if (sc == NULL) { - PyErr_SetFromOSErrnoWithSyscall("OpenSCManager"); + psutil_PyErr_SetFromOSErrnoWithSyscall("OpenSCManager"); return NULL; } hService = OpenService(sc, service_name, access); if (hService == NULL) { - PyErr_SetFromOSErrnoWithSyscall("OpenService"); + psutil_PyErr_SetFromOSErrnoWithSyscall("OpenService"); CloseServiceHandle(sc); return NULL; } @@ -113,7 +113,7 @@ psutil_winservice_enumerate(PyObject *self, PyObject *args) { sc = OpenSCManager(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE); if (sc == NULL) { - PyErr_SetFromOSErrnoWithSyscall("OpenSCManager"); + psutil_PyErr_SetFromOSErrnoWithSyscall("OpenSCManager"); return NULL; } @@ -211,13 +211,13 @@ psutil_winservice_query_config(PyObject *self, PyObject *args) { bytesNeeded = 0; QueryServiceConfigW(hService, NULL, 0, &bytesNeeded); if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { - PyErr_SetFromOSErrnoWithSyscall("QueryServiceConfigW"); + psutil_PyErr_SetFromOSErrnoWithSyscall("QueryServiceConfigW"); goto error; } qsc = (QUERY_SERVICE_CONFIGW *)malloc(bytesNeeded); ok = QueryServiceConfigW(hService, qsc, bytesNeeded, &bytesNeeded); if (ok == 0) { - PyErr_SetFromOSErrnoWithSyscall("QueryServiceConfigW"); + psutil_PyErr_SetFromOSErrnoWithSyscall("QueryServiceConfigW"); goto error; } @@ -303,7 +303,7 @@ psutil_winservice_query_status(PyObject *self, PyObject *args) { return Py_BuildValue("s", ""); } if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { - PyErr_SetFromOSErrnoWithSyscall("QueryServiceStatusEx"); + psutil_PyErr_SetFromOSErrnoWithSyscall("QueryServiceStatusEx"); goto error; } ssp = (SERVICE_STATUS_PROCESS *)HeapAlloc( @@ -317,7 +317,7 @@ psutil_winservice_query_status(PyObject *self, PyObject *args) { ok = QueryServiceStatusEx(hService, SC_STATUS_PROCESS_INFO, (LPBYTE)ssp, bytesNeeded, &bytesNeeded); if (ok == 0) { - PyErr_SetFromOSErrnoWithSyscall("QueryServiceStatusEx"); + psutil_PyErr_SetFromOSErrnoWithSyscall("QueryServiceStatusEx"); goto error; } @@ -375,7 +375,7 @@ psutil_winservice_query_descr(PyObject *self, PyObject *args) { return Py_BuildValue("s", ""); } if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { - PyErr_SetFromOSErrnoWithSyscall("QueryServiceConfig2W"); + psutil_PyErr_SetFromOSErrnoWithSyscall("QueryServiceConfig2W"); goto error; } @@ -383,7 +383,7 @@ psutil_winservice_query_descr(PyObject *self, PyObject *args) { ok = QueryServiceConfig2W(hService, SERVICE_CONFIG_DESCRIPTION, (LPBYTE)scd, bytesNeeded, &bytesNeeded); if (ok == 0) { - PyErr_SetFromOSErrnoWithSyscall("QueryServiceConfig2W"); + psutil_PyErr_SetFromOSErrnoWithSyscall("QueryServiceConfig2W"); goto error; } @@ -429,7 +429,7 @@ psutil_winservice_start(PyObject *self, PyObject *args) { } ok = StartService(hService, 0, NULL); if (ok == 0) { - PyErr_SetFromOSErrnoWithSyscall("StartService"); + psutil_PyErr_SetFromOSErrnoWithSyscall("StartService"); goto error; } @@ -466,7 +466,7 @@ psutil_winservice_stop(PyObject *self, PyObject *args) { ok = ControlService(hService, SERVICE_CONTROL_STOP, &ssp); Py_END_ALLOW_THREADS if (ok == 0) { - PyErr_SetFromOSErrnoWithSyscall("ControlService"); + psutil_PyErr_SetFromOSErrnoWithSyscall("ControlService"); goto error; } diff --git a/psutil/arch/windows/sys.c b/psutil/arch/windows/sys.c index 3e12e71b7..ada684f6f 100644 --- a/psutil/arch/windows/sys.c +++ b/psutil/arch/windows/sys.c @@ -70,7 +70,7 @@ psutil_users(PyObject *self, PyObject *args) { // On Windows Nano server, the Wtsapi32 API can be present, but return WinError 120. return py_retlist; } - PyErr_SetFromOSErrnoWithSyscall("WTSEnumerateSessionsW"); + psutil_PyErr_SetFromOSErrnoWithSyscall("WTSEnumerateSessionsW"); goto error; } @@ -93,7 +93,9 @@ psutil_users(PyObject *self, PyObject *args) { bytes = 0; if (WTSQuerySessionInformationW(hServer, sessionId, WTSUserName, &buffer_user, &bytes) == 0) { - PyErr_SetFromOSErrnoWithSyscall("WTSQuerySessionInformationW"); + psutil_PyErr_SetFromOSErrnoWithSyscall( + "WTSQuerySessionInformationW" + ); goto error; } if (bytes <= 2) @@ -103,7 +105,9 @@ psutil_users(PyObject *self, PyObject *args) { bytes = 0; if (WTSQuerySessionInformationW(hServer, sessionId, WTSClientAddress, &buffer_addr, &bytes) == 0) { - PyErr_SetFromOSErrnoWithSyscall("WTSQuerySessionInformationW"); + psutil_PyErr_SetFromOSErrnoWithSyscall( + "WTSQuerySessionInformationW" + ); goto error; } @@ -130,7 +134,9 @@ psutil_users(PyObject *self, PyObject *args) { bytes = 0; if (WTSQuerySessionInformationW(hServer, sessionId, WTSSessionInfo, &buffer_info, &bytes) == 0) { - PyErr_SetFromOSErrnoWithSyscall("WTSQuerySessionInformationW"); + psutil_PyErr_SetFromOSErrnoWithSyscall( + "WTSQuerySessionInformationW" + ); goto error; } wts_info = (PWTSINFOW)buffer_info; diff --git a/psutil/arch/windows/wmi.c b/psutil/arch/windows/wmi.c index fc7a66529..2cf7e8a59 100644 --- a/psutil/arch/windows/wmi.c +++ b/psutil/arch/windows/wmi.c @@ -80,7 +80,7 @@ psutil_init_loadavg_counter(PyObject *self, PyObject *args) { event = CreateEventW(NULL, FALSE, FALSE, L"LoadUpdateEvent"); if (event == NULL) { - PyErr_SetFromOSErrnoWithSyscall("CreateEventW"); + psutil_PyErr_SetFromOSErrnoWithSyscall("CreateEventW"); return NULL; } @@ -100,7 +100,7 @@ psutil_init_loadavg_counter(PyObject *self, PyObject *args) { WT_EXECUTEDEFAULT); if (ret == 0) { - PyErr_SetFromOSErrnoWithSyscall("RegisterWaitForSingleObject"); + psutil_PyErr_SetFromOSErrnoWithSyscall("RegisterWaitForSingleObject"); return NULL; } diff --git a/psutil/tests/README.rst b/psutil/tests/README.rst index 9dca11867..8e5b6e7d8 100644 --- a/psutil/tests/README.rst +++ b/psutil/tests/README.rst @@ -1,14 +1,35 @@ Instructions for running tests ============================== -* There are two ways of running tests. As a "user", if psutil is already - installed and you just want to test it works:: +There are 2 ways of running tests. If you have the source code: + +.. code-block:: bash + + make install-pydeps-test # install pytest + make test + +If you don't have the source code, and just want to test psutil installation. +This will work also if ``pytest`` module is not installed (e.g. production +environments) by using unittest's test runner: + +.. code-block:: bash python -m psutil.tests - As a "developer", if you have a copy of the source code and you wish to hack - on psutil:: +To run tests in parallel (faster): + +.. code-block:: bash + + make test-parallel + +Run a specific test: + +.. code-block:: bash + + make test ARGS=psutil.tests.test_system.TestDiskAPIs + +Test C extension memory leaks: + +.. code-block:: bash - make setup-dev-env # install missing third-party deps - make test # serial run - make test-parallel # parallel run + make test-memleaks diff --git a/psutil/tests/__init__.py b/psutil/tests/__init__.py index dccb9e4b2..dd3d53647 100644 --- a/psutil/tests/__init__.py +++ b/psutil/tests/__init__.py @@ -14,7 +14,6 @@ import errno import functools import gc -import inspect import os import platform import random @@ -37,6 +36,12 @@ from socket import AF_INET6 from socket import SOCK_STREAM + +try: + import pytest +except ImportError: + pytest = None + import psutil from psutil import AIX from psutil import LINUX @@ -70,6 +75,8 @@ if PY3: import enum else: + import unittest2 as unittest + enum = None if POSIX: @@ -87,7 +94,7 @@ "HAS_IONICE", "HAS_MEMORY_MAPS", "HAS_PROC_CPU_NUM", "HAS_RLIMIT", "HAS_SENSORS_BATTERY", "HAS_BATTERY", "HAS_SENSORS_FANS", "HAS_SENSORS_TEMPERATURES", "HAS_NET_CONNECTIONS_UNIX", "MACOS_11PLUS", - "MACOS_12PLUS", "COVERAGE", 'AARCH64', "QEMU_USER", + "MACOS_12PLUS", "COVERAGE", 'AARCH64', "QEMU_USER", "PYTEST_PARALLEL", # subprocesses 'pyrun', 'terminate', 'reap_children', 'spawn_testproc', 'spawn_zombie', 'spawn_children_pair', @@ -97,7 +104,7 @@ 'unittest', 'skip_on_access_denied', 'skip_on_not_implemented', 'retry_on_failure', 'TestMemoryLeak', 'PsutilTestCase', 'process_namespace', 'system_namespace', 'print_sysinfo', - 'is_win_secure_system_proc', + 'is_win_secure_system_proc', 'fake_pytest', # fs utils 'chdir', 'safe_rmpath', 'create_py_exe', 'create_c_exe', 'get_testfn', # os @@ -128,6 +135,7 @@ GITHUB_ACTIONS = 'GITHUB_ACTIONS' in os.environ or 'CIBUILDWHEEL' in os.environ CI_TESTING = APPVEYOR or GITHUB_ACTIONS COVERAGE = 'COVERAGE_RUN' in os.environ +PYTEST_PARALLEL = "PYTEST_XDIST_WORKER" in os.environ # `make test-parallel` if LINUX and GITHUB_ACTIONS: with open('/proc/1/cmdline') as f: QEMU_USER = "/bin/qemu-" in f.read() @@ -470,7 +478,7 @@ def spawn_zombie(): zpid = int(conn.recv(1024)) _pids_started.add(zpid) zombie = psutil.Process(zpid) - call_until(zombie.status, "ret == psutil.STATUS_ZOMBIE") + call_until(lambda: zombie.status() == psutil.STATUS_ZOMBIE) return (parent, zombie) finally: conn.close() @@ -521,7 +529,7 @@ def sh(cmd, **kwds): else: stdout, stderr = p.communicate() if p.returncode != 0: - raise RuntimeError(stderr) + raise RuntimeError(stdout + stderr) if stderr: warn(stderr) if stdout.endswith('\n'): @@ -790,12 +798,10 @@ def wait_for_file(fname, delete=True, empty=False): timeout=GLOBAL_TIMEOUT, interval=0.001, ) -def call_until(fun, expr): - """Keep calling function for timeout secs and exit if eval() - expression is True. - """ +def call_until(fun): + """Keep calling function until it evaluates to True.""" ret = fun() - assert eval(expr) # noqa + assert ret return ret @@ -872,7 +878,7 @@ 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"): - raise unittest.SkipTest("gcc is not installed") + raise pytest.skip("gcc is not installed") if c_code is None: c_code = textwrap.dedent(""" #include @@ -913,25 +919,88 @@ def get_testfn(suffix="", dir=None): # =================================================================== -class TestCase(unittest.TestCase): +class fake_pytest: + """A class that mimics some basic pytest APIs. This is meant for + when unit tests are run in production, where pytest may not be + installed. Still, the user can test psutil installation via: - # Print a full path representation of the single unit tests - # being run. - def __str__(self): - fqmod = self.__class__.__module__ - if not fqmod.startswith('psutil.'): - fqmod = 'psutil.tests.' + fqmod - return "%s.%s.%s" % ( - fqmod, - self.__class__.__name__, - self._testMethodName, + $ python3 -m psutil.tests + """ + + @staticmethod + def main(*args, **kw): # noqa ARG004 + """Mimics pytest.main(). It has the same effect as running + `python3 -m unittest -v` from the project root directory. + """ + suite = unittest.TestLoader().discover(HERE) + unittest.TextTestRunner(verbosity=2).run(suite) + warnings.warn( + "Fake pytest module was used. Test results may be inaccurate.", + UserWarning, + stacklevel=1, ) + return suite + + @staticmethod + def raises(exc, match=None): + """Mimics `pytest.raises`.""" + + class ExceptionInfo: + _exc = None + + @property + def value(self): + return self._exc + + @contextlib.contextmanager + def context(exc, match=None): + einfo = ExceptionInfo() + try: + yield einfo + except exc as err: + if match and not re.search(match, str(err)): + msg = '"{}" does not match "{}"'.format(match, str(err)) + raise AssertionError(msg) + einfo._exc = err + else: + raise AssertionError("%r not raised" % exc) - # assertRaisesRegexp renamed to assertRaisesRegex in 3.3; - # add support for the new name. - if not hasattr(unittest.TestCase, 'assertRaisesRegex'): - assertRaisesRegex = unittest.TestCase.assertRaisesRegexp # noqa + return context(exc, match=match) + @staticmethod + def warns(warning, match=None): + """Mimics `pytest.warns`.""" + if match: + return unittest.TestCase().assertWarnsRegex(warning, match) + return unittest.TestCase().assertWarns(warning) + + @staticmethod + def skip(reason=""): + """Mimics `unittest.SkipTest`.""" + raise unittest.SkipTest(reason) + + class mark: + + @staticmethod + def skipif(condition, reason=""): + """Mimics `@pytest.mark.skipif` decorator.""" + return unittest.skipIf(condition, reason) + + class xdist_group: + """Mimics `@pytest.mark.xdist_group` decorator (no-op).""" + + def __init__(self, name=None): + pass + + def __call__(self, cls_or_meth): + return cls_or_meth + + +if pytest is None: + pytest = fake_pytest + + +class TestCase(unittest.TestCase): # ...otherwise multiprocessing.Pool complains if not PY3: @@ -950,7 +1019,8 @@ def subTest(self, *args, **kw): class PsutilTestCase(TestCase): """Test class providing auto-cleanup wrappers on top of process - test utilities. + test utilities. All test classes should derive from this one, even + if we use pytest. """ def get_testfn(self, suffix="", dir=None): @@ -982,29 +1052,29 @@ def pyrun(self, *args, **kwds): return sproc def _check_proc_exc(self, proc, exc): - self.assertIsInstance(exc, psutil.Error) - self.assertEqual(exc.pid, proc.pid) - self.assertEqual(exc.name, proc._name) + assert isinstance(exc, psutil.Error) + assert exc.pid == proc.pid + assert exc.name == proc._name if exc.name: - self.assertNotEqual(exc.name, "") + assert exc.name if isinstance(exc, psutil.ZombieProcess): - self.assertEqual(exc.ppid, proc._ppid) + assert exc.ppid == proc._ppid if exc.ppid is not None: - self.assertGreaterEqual(exc.ppid, 0) + assert exc.ppid >= 0 str(exc) repr(exc) def assertPidGone(self, pid): - with self.assertRaises(psutil.NoSuchProcess) as cm: + with pytest.raises(psutil.NoSuchProcess) as cm: try: psutil.Process(pid) except psutil.ZombieProcess: raise AssertionError("wasn't supposed to raise ZombieProcess") - self.assertEqual(cm.exception.pid, pid) - self.assertEqual(cm.exception.name, None) + assert cm.value.pid == pid + assert cm.value.name is None assert not psutil.pid_exists(pid), pid - self.assertNotIn(pid, psutil.pids()) - self.assertNotIn(pid, [x.pid for x in psutil.process_iter()]) + assert pid not in psutil.pids() + assert pid not in [x.pid for x in psutil.process_iter()] def assertProcessGone(self, proc): self.assertPidGone(proc.pid) @@ -1030,21 +1100,21 @@ def assertProcessZombie(self, proc): clone = psutil.Process(proc.pid) # Cloned zombie on Open/NetBSD has null creation time, see: # https://github.com/giampaolo/psutil/issues/2287 - self.assertEqual(proc, clone) + assert proc == clone if not (OPENBSD or NETBSD): - self.assertEqual(hash(proc), hash(clone)) + assert hash(proc) == hash(clone) # Its status always be querable. - self.assertEqual(proc.status(), psutil.STATUS_ZOMBIE) + assert proc.status() == psutil.STATUS_ZOMBIE # It should be considered 'running'. assert proc.is_running() assert psutil.pid_exists(proc.pid) # as_dict() shouldn't crash. proc.as_dict() # It should show up in pids() and process_iter(). - self.assertIn(proc.pid, psutil.pids()) - self.assertIn(proc.pid, [x.pid for x in psutil.process_iter()]) + assert proc.pid in psutil.pids() + assert proc.pid in [x.pid for x in psutil.process_iter()] psutil._pmap = {} - self.assertIn(proc.pid, [x.pid for x in psutil.process_iter()]) + assert proc.pid in [x.pid for x in psutil.process_iter()] # Call all methods. ns = process_namespace(proc) for fun, name in ns.iter(ns.all, clear_cache=True): @@ -1055,15 +1125,15 @@ def assertProcessZombie(self, proc): self._check_proc_exc(proc, exc) if LINUX: # https://github.com/giampaolo/psutil/pull/2288 - with self.assertRaises(psutil.ZombieProcess) as cm: + with pytest.raises(psutil.ZombieProcess) as cm: proc.cmdline() - self._check_proc_exc(proc, cm.exception) - with self.assertRaises(psutil.ZombieProcess) as cm: + self._check_proc_exc(proc, cm.value) + with pytest.raises(psutil.ZombieProcess) as cm: proc.exe() - self._check_proc_exc(proc, cm.exception) - with self.assertRaises(psutil.ZombieProcess) as cm: + self._check_proc_exc(proc, cm.value) + with pytest.raises(psutil.ZombieProcess) as cm: proc.memory_maps() - self._check_proc_exc(proc, cm.exception) + self._check_proc_exc(proc, cm.value) # Zombie cannot be signaled or terminated. proc.suspend() proc.resume() @@ -1071,10 +1141,10 @@ def assertProcessZombie(self, proc): proc.kill() assert proc.is_running() assert psutil.pid_exists(proc.pid) - self.assertIn(proc.pid, psutil.pids()) - self.assertIn(proc.pid, [x.pid for x in psutil.process_iter()]) + assert proc.pid in psutil.pids() + assert proc.pid in [x.pid for x in psutil.process_iter()] psutil._pmap = {} - self.assertIn(proc.pid, [x.pid for x in psutil.process_iter()]) + assert proc.pid in [x.pid for x in psutil.process_iter()] # Its parent should 'see' it (edit: not true on BSD and MACOS). # descendants = [x.pid for x in psutil.Process().children( @@ -1091,7 +1161,7 @@ def assertProcessZombie(self, proc): # self.assertEqual(proc.ppid(), os.getpid()) -@unittest.skipIf(PYPY, "unreliable on PYPY") +@pytest.mark.skipif(PYPY, reason="unreliable on PYPY") class TestMemoryLeak(PsutilTestCase): """Test framework class for detecting function memory leaks, typically functions implemented in C which forgot to free() memory @@ -1188,7 +1258,7 @@ def _call_ntimes(self, fun, times): del x, ret gc.collect(generation=1) mem2 = self._get_mem() - self.assertEqual(gc.garbage, []) + assert gc.garbage == [] diff = mem2 - mem1 # can also be negative return diff @@ -1355,11 +1425,12 @@ def print_sysinfo(): print("=" * 70, file=sys.stderr) # NOQA sys.stdout.flush() - if WINDOWS: - os.system("tasklist") - elif which("ps"): - os.system("ps aux") - print("=" * 70, file=sys.stderr) # NOQA + # if WINDOWS: + # os.system("tasklist") + # elif which("ps"): + # os.system("ps aux") + # print("=" * 70, file=sys.stderr) # NOQA + sys.stdout.flush() @@ -1598,16 +1669,6 @@ def iter(ls): test_class_coverage = process_namespace.test_class_coverage -def serialrun(klass): - """A decorator to mark a TestCase class. When running parallel tests, - class' unit tests will be run serially (1 process). - """ - # assert issubclass(klass, unittest.TestCase), klass - assert inspect.isclass(klass), klass - klass._serialrun = True - return klass - - def retry_on_failure(retries=NO_RETRIES): """Decorator which runs a test function and retries N times before actually failing. @@ -1633,7 +1694,7 @@ def wrapper(*args, **kwargs): if only_if is not None: if not only_if: raise - raise unittest.SkipTest("raises AccessDenied") + raise pytest.skip("raises AccessDenied") return wrapper @@ -1656,7 +1717,7 @@ def wrapper(*args, **kwargs): "%r was skipped because it raised NotImplementedError" % fun.__name__ ) - raise unittest.SkipTest(msg) + raise pytest.skip(msg) return wrapper diff --git a/psutil/tests/__main__.py b/psutil/tests/__main__.py index 2460abdb3..ce6fc24c7 100644 --- a/psutil/tests/__main__.py +++ b/psutil/tests/__main__.py @@ -6,7 +6,7 @@ $ python -m psutil.tests. """ -from .runner import main +from psutil.tests import pytest -main() +pytest.main(["-v", "-s", "--tb=short"]) diff --git a/psutil/tests/runner.py b/psutil/tests/runner.py deleted file mode 100755 index 3b28b64f1..000000000 --- a/psutil/tests/runner.py +++ /dev/null @@ -1,385 +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. - -"""Unit test runner, providing new features on top of unittest module: -- colourized output -- parallel run (UNIX only) -- print failures/tracebacks on CTRL+C -- re-run failed tests only (make test-failed). - -Invocation examples: -- make test -- make test-failed - -Parallel: -- make test-parallel -- make test-process ARGS=--parallel -""" - -from __future__ import print_function - -import atexit -import optparse -import os -import sys -import textwrap -import time -import unittest - - -try: - import ctypes -except ImportError: - ctypes = None - -try: - import concurrencytest # pip install concurrencytest -except ImportError: - concurrencytest = None - -import psutil -from psutil._common import hilite -from psutil._common import print_color -from psutil._common import term_supports_colors -from psutil._compat import super -from psutil.tests import CI_TESTING -from psutil.tests import import_module_by_path -from psutil.tests import print_sysinfo -from psutil.tests import reap_children -from psutil.tests import safe_rmpath - - -VERBOSITY = 2 -FAILED_TESTS_FNAME = '.failed-tests.txt' -NWORKERS = psutil.cpu_count() or 1 -USE_COLORS = not CI_TESTING and term_supports_colors() - -HERE = os.path.abspath(os.path.dirname(__file__)) -loadTestsFromTestCase = ( # noqa: N816 - unittest.defaultTestLoader.loadTestsFromTestCase -) - - -def cprint(msg, color, bold=False, file=None): - if file is None: - file = sys.stderr if color == 'red' else sys.stdout - if USE_COLORS: - print_color(msg, color, bold=bold, file=file) - else: - print(msg, file=file) - - -class TestLoader: - - testdir = HERE - skip_files = ['test_memleaks.py'] - if "WHEELHOUSE_UPLOADER_USERNAME" in os.environ: - skip_files.extend(['test_osx.py', 'test_linux.py', 'test_posix.py']) - - def _get_testmods(self): - return [ - os.path.join(self.testdir, x) - for x in os.listdir(self.testdir) - if x.startswith('test_') - and x.endswith('.py') - and x not in self.skip_files - ] - - def _iter_testmod_classes(self): - """Iterate over all test files in this directory and return - all TestCase classes in them. - """ - for path in self._get_testmods(): - mod = import_module_by_path(path) - for name in dir(mod): - obj = getattr(mod, name) - if isinstance(obj, type) and issubclass( - obj, unittest.TestCase - ): - yield obj - - def all(self): - suite = unittest.TestSuite() - for obj in self._iter_testmod_classes(): - test = loadTestsFromTestCase(obj) - suite.addTest(test) - return suite - - def last_failed(self): - # ...from previously failed test run - suite = unittest.TestSuite() - if not os.path.isfile(FAILED_TESTS_FNAME): - return suite - with open(FAILED_TESTS_FNAME) as f: - names = f.read().split() - for n in names: - test = unittest.defaultTestLoader.loadTestsFromName(n) - suite.addTest(test) - return suite - - def from_name(self, name): - if name.endswith('.py'): - name = os.path.splitext(os.path.basename(name))[0] - return unittest.defaultTestLoader.loadTestsFromName(name) - - -class ColouredResult(unittest.TextTestResult): - def addSuccess(self, test): - unittest.TestResult.addSuccess(self, test) - cprint("OK", "green") - - def addError(self, test, err): - unittest.TestResult.addError(self, test, err) - cprint("ERROR", "red", bold=True) - - def addFailure(self, test, err): - unittest.TestResult.addFailure(self, test, err) - cprint("FAIL", "red") - - def addSkip(self, test, reason): - unittest.TestResult.addSkip(self, test, reason) - cprint("skipped: %s" % reason.strip(), "brown") - - def printErrorList(self, flavour, errors): - flavour = hilite(flavour, "red", bold=flavour == 'ERROR') - super().printErrorList(flavour, errors) - - -class ColouredTextRunner(unittest.TextTestRunner): - """A coloured text runner which also prints failed tests on - KeyboardInterrupt and save failed tests in a file so that they can - be re-run. - """ - - resultclass = ColouredResult if USE_COLORS else unittest.TextTestResult - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.failed_tnames = set() - - def _makeResult(self): - # Store result instance so that it can be accessed on - # KeyboardInterrupt. - self.result = super()._makeResult() - return self.result - - def _write_last_failed(self): - if self.failed_tnames: - with open(FAILED_TESTS_FNAME, "w") as f: - for tname in self.failed_tnames: - f.write(tname + '\n') - - def _save_result(self, result): - if not result.wasSuccessful(): - for t in result.errors + result.failures: - tname = t[0].id() - self.failed_tnames.add(tname) - - def _run(self, suite): - try: - result = super().run(suite) - except (KeyboardInterrupt, SystemExit): - result = self.runner.result - result.printErrors() - raise sys.exit(1) - else: - self._save_result(result) - return result - - def _exit(self, success): - if success: - cprint("SUCCESS", "green", bold=True) - safe_rmpath(FAILED_TESTS_FNAME) - sys.exit(0) - else: - cprint("FAILED", "red", bold=True) - self._write_last_failed() - sys.exit(1) - - def run(self, suite): - result = self._run(suite) - self._exit(result.wasSuccessful()) - - -class ParallelRunner(ColouredTextRunner): - @staticmethod - def _parallelize(suite): - def fdopen(fd, mode, *kwds): - stream = orig_fdopen(fd, mode) - atexit.register(stream.close) - return stream - - # Monkey patch concurrencytest lib bug (fdopen() stream not closed). - # https://github.com/cgoldberg/concurrencytest/issues/11 - orig_fdopen = os.fdopen - concurrencytest.os.fdopen = fdopen - forker = concurrencytest.fork_for_tests(NWORKERS) - return concurrencytest.ConcurrentTestSuite(suite, forker) - - @staticmethod - def _split_suite(suite): - serial = unittest.TestSuite() - parallel = unittest.TestSuite() - for test in suite: - if test.countTestCases() == 0: - continue - if isinstance(test, unittest.TestSuite): - test_class = test._tests[0].__class__ - elif isinstance(test, unittest.TestCase): - test_class = test - else: - raise TypeError("can't recognize type %r" % test) - - if getattr(test_class, '_serialrun', False): - serial.addTest(test) - else: - parallel.addTest(test) - return (serial, parallel) - - def run(self, suite): - ser_suite, par_suite = self._split_suite(suite) - par_suite = self._parallelize(par_suite) - - # run parallel - cprint( - "starting parallel tests using %s workers" % NWORKERS, - "green", - bold=True, - ) - t = time.time() - par = self._run(par_suite) - par_elapsed = time.time() - t - - # At this point we should have N zombies (the workers), which - # will disappear with wait(). - orphans = psutil.Process().children() - _gone, alive = psutil.wait_procs(orphans, timeout=1) - if alive: - cprint("alive processes %s" % alive, "red") - reap_children() - - # run serial - t = time.time() - ser = self._run(ser_suite) - ser_elapsed = time.time() - t - - # print - if not par.wasSuccessful() and ser_suite.countTestCases() > 0: - par.printErrors() # print them again at the bottom - par_fails, par_errs, par_skips = map( - len, (par.failures, par.errors, par.skipped) - ) - ser_fails, ser_errs, ser_skips = map( - len, (ser.failures, ser.errors, ser.skipped) - ) - print( - textwrap.dedent( - """ - +----------+----------+----------+----------+----------+----------+ - | | total | failures | errors | skipped | time | - +----------+----------+----------+----------+----------+----------+ - | parallel | %3s | %3s | %3s | %3s | %.2fs | - +----------+----------+----------+----------+----------+----------+ - | serial | %3s | %3s | %3s | %3s | %.2fs | - +----------+----------+----------+----------+----------+----------+ - """ - % ( - par.testsRun, - par_fails, - par_errs, - par_skips, - par_elapsed, - ser.testsRun, - ser_fails, - ser_errs, - ser_skips, - ser_elapsed, - ) - ) - ) - print( - "Ran %s tests in %.3fs using %s workers" - % ( - par.testsRun + ser.testsRun, - par_elapsed + ser_elapsed, - NWORKERS, - ) - ) - ok = par.wasSuccessful() and ser.wasSuccessful() - self._exit(ok) - - -def get_runner(parallel=False): - def warn(msg): - cprint(msg + " Running serial tests instead.", "red") - - if parallel: - if psutil.WINDOWS: - warn("Can't run parallel tests on Windows.") - elif concurrencytest is None: - warn("concurrencytest module is not installed.") - elif NWORKERS == 1: - warn("Only 1 CPU available.") - else: - return ParallelRunner(verbosity=VERBOSITY) - return ColouredTextRunner(verbosity=VERBOSITY) - - -# Used by test_*,py modules. -def run_from_name(name): - if CI_TESTING: - print_sysinfo() - suite = TestLoader().from_name(name) - runner = get_runner() - runner.run(suite) - - -def setup(): - psutil._set_debug(True) - - -def main(): - setup() - usage = "python3 -m psutil.tests [opts] [test-name]" - parser = optparse.OptionParser(usage=usage, description="run unit tests") - parser.add_option( - "--last-failed", - action="store_true", - default=False, - help="only run last failed tests", - ) - parser.add_option( - "--parallel", - action="store_true", - default=False, - help="run tests in parallel", - ) - opts, args = parser.parse_args() - - if not opts.last_failed: - safe_rmpath(FAILED_TESTS_FNAME) - - # loader - loader = TestLoader() - if args: - if len(args) > 1: - parser.print_usage() - return sys.exit(1) - else: - suite = loader.from_name(args[0]) - elif opts.last_failed: - suite = loader.last_failed() - else: - suite = loader.all() - - if CI_TESTING: - print_sysinfo() - runner = get_runner(opts.parallel) - runner.run(suite) - - -if __name__ == '__main__': - main() diff --git a/psutil/tests/test_aix.py b/psutil/tests/test_aix.py index e7e0c8aa5..68d467b18 100755 --- a/psutil/tests/test_aix.py +++ b/psutil/tests/test_aix.py @@ -9,15 +9,15 @@ """AIX specific tests.""" import re -import unittest import psutil from psutil import AIX from psutil.tests import PsutilTestCase +from psutil.tests import pytest from psutil.tests import sh -@unittest.skipIf(not AIX, "AIX only") +@pytest.mark.skipif(not AIX, reason="AIX only") class AIXSpecificTestCase(PsutilTestCase): def test_virtual_memory(self): out = sh('/usr/bin/svmon -O unit=KB') @@ -26,9 +26,7 @@ def test_virtual_memory(self): re_pattern += r"(?P<%s>\S+)\s+" % (field,) matchobj = re.search(re_pattern, out) - self.assertIsNotNone( - matchobj, "svmon command returned unexpected output" - ) + assert matchobj is not None KB = 1024 total = int(matchobj.group("size")) * KB @@ -42,16 +40,10 @@ def test_virtual_memory(self): # we're seeing differences of ~1.2 MB. 2 MB is still a good tolerance # when compared to GBs. TOLERANCE_SYS_MEM = 2 * KB * KB # 2 MB - self.assertEqual(psutil_result.total, total) - self.assertAlmostEqual( - psutil_result.used, used, delta=TOLERANCE_SYS_MEM - ) - self.assertAlmostEqual( - psutil_result.available, available, delta=TOLERANCE_SYS_MEM - ) - self.assertAlmostEqual( - psutil_result.free, free, delta=TOLERANCE_SYS_MEM - ) + assert psutil_result.total == total + assert abs(psutil_result.used - used) < TOLERANCE_SYS_MEM + assert abs(psutil_result.available - available) < TOLERANCE_SYS_MEM + assert abs(psutil_result.free - free) < TOLERANCE_SYS_MEM def test_swap_memory(self): out = sh('/usr/sbin/lsps -a') @@ -67,16 +59,14 @@ def test_swap_memory(self): out, ) - self.assertIsNotNone( - matchobj, "lsps command returned unexpected output" - ) + assert matchobj is not None total_mb = int(matchobj.group("size")) MB = 1024**2 psutil_result = psutil.swap_memory() # we divide our result by MB instead of multiplying the lsps value by # MB because lsps may round down, so we round down too - self.assertEqual(int(psutil_result.total / MB), total_mb) + assert int(psutil_result.total / MB) == total_mb def test_cpu_stats(self): out = sh('/usr/bin/mpstat -a') @@ -90,48 +80,36 @@ def test_cpu_stats(self): re_pattern += r"(?P<%s>\S+)\s+" % (field,) matchobj = re.search(re_pattern, out) - self.assertIsNotNone( - matchobj, "mpstat command returned unexpected output" - ) + assert matchobj is not None # numbers are usually in the millions so 1000 is ok for tolerance CPU_STATS_TOLERANCE = 1000 psutil_result = psutil.cpu_stats() - self.assertAlmostEqual( - psutil_result.ctx_switches, - int(matchobj.group("cs")), - delta=CPU_STATS_TOLERANCE, + assert ( + abs(psutil_result.ctx_switches - int(matchobj.group("cs"))) + < CPU_STATS_TOLERANCE ) - self.assertAlmostEqual( - psutil_result.syscalls, - int(matchobj.group("sysc")), - delta=CPU_STATS_TOLERANCE, + assert ( + abs(psutil_result.syscalls - int(matchobj.group("sysc"))) + < CPU_STATS_TOLERANCE ) - self.assertAlmostEqual( - psutil_result.interrupts, - int(matchobj.group("dev")), - delta=CPU_STATS_TOLERANCE, + assert ( + abs(psutil_result.interrupts - int(matchobj.group("dev"))) + < CPU_STATS_TOLERANCE ) - self.assertAlmostEqual( - psutil_result.soft_interrupts, - int(matchobj.group("soft")), - delta=CPU_STATS_TOLERANCE, + assert ( + abs(psutil_result.soft_interrupts - int(matchobj.group("soft"))) + < CPU_STATS_TOLERANCE ) def test_cpu_count_logical(self): out = sh('/usr/bin/mpstat -a') mpstat_lcpu = int(re.search(r"lcpu=(\d+)", out).group(1)) psutil_lcpu = psutil.cpu_count(logical=True) - self.assertEqual(mpstat_lcpu, psutil_lcpu) + assert mpstat_lcpu == psutil_lcpu def test_net_if_addrs_names(self): out = sh('/etc/ifconfig -l') ifconfig_names = set(out.split()) psutil_names = set(psutil.net_if_addrs().keys()) - self.assertSetEqual(ifconfig_names, psutil_names) - - -if __name__ == '__main__': - from psutil.tests.runner import run_from_name - - run_from_name(__file__) + assert ifconfig_names == psutil_names diff --git a/psutil/tests/test_bsd.py b/psutil/tests/test_bsd.py index 8512b4f9e..2fd1015d7 100755 --- a/psutil/tests/test_bsd.py +++ b/psutil/tests/test_bsd.py @@ -14,7 +14,6 @@ import os import re import time -import unittest import psutil from psutil import BSD @@ -24,6 +23,7 @@ from psutil.tests import HAS_BATTERY from psutil.tests import TOLERANCE_SYS_MEM from psutil.tests import PsutilTestCase +from psutil.tests import pytest from psutil.tests import retry_on_failure from psutil.tests import sh from psutil.tests import spawn_testproc @@ -73,7 +73,7 @@ def muse(field): # ===================================================================== -@unittest.skipIf(not BSD, "BSD only") +@pytest.mark.skipif(not BSD, reason="BSD only") class BSDTestCase(PsutilTestCase): """Generic tests common to all BSD variants.""" @@ -85,7 +85,7 @@ def setUpClass(cls): def tearDownClass(cls): terminate(cls.pid) - @unittest.skipIf(NETBSD, "-o lstart doesn't work on NETBSD") + @pytest.mark.skipif(NETBSD, reason="-o lstart doesn't work on NETBSD") def test_process_create_time(self): output = sh("ps -o lstart -p %s" % self.pid) start_ps = output.replace('STARTED', '').strip() @@ -93,7 +93,7 @@ def test_process_create_time(self): start_psutil = time.strftime( "%a %b %e %H:%M:%S %Y", time.localtime(start_psutil) ) - self.assertEqual(start_ps, start_psutil) + assert start_ps == start_psutil def test_disks(self): # test psutil.disk_usage() and psutil.disk_partitions() @@ -114,26 +114,30 @@ def df(path): for part in psutil.disk_partitions(all=False): usage = psutil.disk_usage(part.mountpoint) dev, total, used, free = df(part.mountpoint) - self.assertEqual(part.device, dev) - self.assertEqual(usage.total, total) + assert part.device == dev + assert usage.total == total # 10 MB tolerance if abs(usage.free - free) > 10 * 1024 * 1024: raise self.fail("psutil=%s, df=%s" % (usage.free, free)) if abs(usage.used - used) > 10 * 1024 * 1024: raise self.fail("psutil=%s, df=%s" % (usage.used, used)) - @unittest.skipIf(not which('sysctl'), "sysctl cmd not available") + @pytest.mark.skipif(not which('sysctl'), reason="sysctl cmd not available") def test_cpu_count_logical(self): syst = sysctl("hw.ncpu") - self.assertEqual(psutil.cpu_count(logical=True), syst) + assert psutil.cpu_count(logical=True) == syst - @unittest.skipIf(not which('sysctl'), "sysctl cmd not available") - @unittest.skipIf(NETBSD, "skipped on NETBSD") # we check /proc/meminfo + @pytest.mark.skipif(not which('sysctl'), reason="sysctl cmd not available") + @pytest.mark.skipif( + NETBSD, reason="skipped on NETBSD" # we check /proc/meminfo + ) def test_virtual_memory_total(self): num = sysctl('hw.physmem') - self.assertEqual(num, psutil.virtual_memory().total) + assert num == psutil.virtual_memory().total - @unittest.skipIf(not which('ifconfig'), "ifconfig cmd not available") + @pytest.mark.skipif( + not which('ifconfig'), reason="ifconfig cmd not available" + ) def test_net_if_stats(self): for name, stats in psutil.net_if_stats().items(): try: @@ -141,11 +145,9 @@ def test_net_if_stats(self): except RuntimeError: pass else: - self.assertEqual(stats.isup, 'RUNNING' in out, msg=out) + assert stats.isup == ('RUNNING' in out) if "mtu" in out: - self.assertEqual( - stats.mtu, int(re.findall(r'mtu (\d+)', out)[0]) - ) + assert stats.mtu == int(re.findall(r'mtu (\d+)', out)[0]) # ===================================================================== @@ -153,7 +155,7 @@ def test_net_if_stats(self): # ===================================================================== -@unittest.skipIf(not FREEBSD, "FREEBSD only") +@pytest.mark.skipif(not FREEBSD, reason="FREEBSD only") class FreeBSDPsutilTestCase(PsutilTestCase): @classmethod def setUpClass(cls): @@ -173,22 +175,19 @@ def test_memory_maps(self): fields = line.split() _, start, stop, _perms, res = fields[:5] map = maps.pop() - self.assertEqual("%s-%s" % (start, stop), map.addr) - self.assertEqual(int(res), map.rss) + assert "%s-%s" % (start, stop) == map.addr + assert int(res) == map.rss if not map.path.startswith('['): - self.assertEqual(fields[10], map.path) + assert fields[10] == map.path def test_exe(self): out = sh('procstat -b %s' % self.pid) - self.assertEqual( - psutil.Process(self.pid).exe(), out.split('\n')[1].split()[-1] - ) + assert psutil.Process(self.pid).exe() == out.split('\n')[1].split()[-1] def test_cmdline(self): out = sh('procstat -c %s' % self.pid) - self.assertEqual( - ' '.join(psutil.Process(self.pid).cmdline()), - ' '.join(out.split('\n')[1].split()[2:]), + assert ' '.join(psutil.Process(self.pid).cmdline()) == ' '.join( + out.split('\n')[1].split()[2:] ) def test_uids_gids(self): @@ -197,12 +196,12 @@ def test_uids_gids(self): p = psutil.Process(self.pid) uids = p.uids() gids = p.gids() - self.assertEqual(uids.real, int(ruid)) - self.assertEqual(uids.effective, int(euid)) - self.assertEqual(uids.saved, int(suid)) - self.assertEqual(gids.real, int(rgid)) - self.assertEqual(gids.effective, int(egid)) - self.assertEqual(gids.saved, int(sgid)) + assert uids.real == int(ruid) + assert uids.effective == int(euid) + assert uids.saved == int(suid) + assert gids.real == int(rgid) + assert gids.effective == int(egid) + assert gids.saved == int(sgid) @retry_on_failure() def test_ctx_switches(self): @@ -214,12 +213,12 @@ def test_ctx_switches(self): if ' voluntary context' in line: pstat_value = int(line.split()[-1]) psutil_value = p.num_ctx_switches().voluntary - self.assertEqual(pstat_value, psutil_value) + assert pstat_value == psutil_value tested.append(None) elif ' involuntary context' in line: pstat_value = int(line.split()[-1]) psutil_value = p.num_ctx_switches().involuntary - self.assertEqual(pstat_value, psutil_value) + assert pstat_value == psutil_value tested.append(None) if len(tested) != 2: raise RuntimeError("couldn't find lines match in procstat out") @@ -234,18 +233,18 @@ def test_cpu_times(self): if 'user time' in line: pstat_value = float('0.' + line.split()[-1].split('.')[-1]) psutil_value = p.cpu_times().user - self.assertEqual(pstat_value, psutil_value) + assert pstat_value == psutil_value tested.append(None) elif 'system time' in line: pstat_value = float('0.' + line.split()[-1].split('.')[-1]) psutil_value = p.cpu_times().system - self.assertEqual(pstat_value, psutil_value) + assert pstat_value == psutil_value tested.append(None) if len(tested) != 2: raise RuntimeError("couldn't find lines match in procstat out") -@unittest.skipIf(not FREEBSD, "FREEBSD only") +@pytest.mark.skipif(not FREEBSD, reason="FREEBSD only") class FreeBSDSystemTestCase(PsutilTestCase): @staticmethod def parse_swapinfo(): @@ -267,8 +266,8 @@ def test_cpu_frequency_against_sysctl(self): try: sysctl_result = int(sysctl(sensor)) except RuntimeError: - raise unittest.SkipTest("frequencies not supported by kernel") - self.assertEqual(psutil.cpu_freq().current, sysctl_result) + raise pytest.skip("frequencies not supported by kernel") + assert psutil.cpu_freq().current == sysctl_result sensor = "dev.cpu.0.freq_levels" sysctl_result = sysctl(sensor) @@ -277,136 +276,114 @@ def test_cpu_frequency_against_sysctl(self): # Ordered highest available to lowest available. max_freq = int(sysctl_result.split()[0].split("/")[0]) min_freq = int(sysctl_result.split()[-1].split("/")[0]) - self.assertEqual(psutil.cpu_freq().max, max_freq) - self.assertEqual(psutil.cpu_freq().min, min_freq) + assert psutil.cpu_freq().max == max_freq + assert psutil.cpu_freq().min == min_freq # --- virtual_memory(); tests against sysctl @retry_on_failure() def test_vmem_active(self): syst = sysctl("vm.stats.vm.v_active_count") * PAGESIZE - self.assertAlmostEqual( - psutil.virtual_memory().active, syst, delta=TOLERANCE_SYS_MEM - ) + assert abs(psutil.virtual_memory().active - syst) < TOLERANCE_SYS_MEM @retry_on_failure() def test_vmem_inactive(self): syst = sysctl("vm.stats.vm.v_inactive_count") * PAGESIZE - self.assertAlmostEqual( - psutil.virtual_memory().inactive, syst, delta=TOLERANCE_SYS_MEM - ) + assert abs(psutil.virtual_memory().inactive - syst) < TOLERANCE_SYS_MEM @retry_on_failure() def test_vmem_wired(self): syst = sysctl("vm.stats.vm.v_wire_count") * PAGESIZE - self.assertAlmostEqual( - psutil.virtual_memory().wired, syst, delta=TOLERANCE_SYS_MEM - ) + assert abs(psutil.virtual_memory().wired - syst) < TOLERANCE_SYS_MEM @retry_on_failure() def test_vmem_cached(self): syst = sysctl("vm.stats.vm.v_cache_count") * PAGESIZE - self.assertAlmostEqual( - psutil.virtual_memory().cached, syst, delta=TOLERANCE_SYS_MEM - ) + assert abs(psutil.virtual_memory().cached - syst) < TOLERANCE_SYS_MEM @retry_on_failure() def test_vmem_free(self): syst = sysctl("vm.stats.vm.v_free_count") * PAGESIZE - self.assertAlmostEqual( - psutil.virtual_memory().free, syst, delta=TOLERANCE_SYS_MEM - ) + assert abs(psutil.virtual_memory().free - syst) < TOLERANCE_SYS_MEM @retry_on_failure() def test_vmem_buffers(self): syst = sysctl("vfs.bufspace") - self.assertAlmostEqual( - psutil.virtual_memory().buffers, syst, delta=TOLERANCE_SYS_MEM - ) + assert abs(psutil.virtual_memory().buffers - syst) < TOLERANCE_SYS_MEM # --- virtual_memory(); tests against muse - @unittest.skipIf(not MUSE_AVAILABLE, "muse not installed") + @pytest.mark.skipif(not MUSE_AVAILABLE, reason="muse not installed") def test_muse_vmem_total(self): num = muse('Total') - self.assertEqual(psutil.virtual_memory().total, num) + assert psutil.virtual_memory().total == num - @unittest.skipIf(not MUSE_AVAILABLE, "muse not installed") + @pytest.mark.skipif(not MUSE_AVAILABLE, reason="muse not installed") @retry_on_failure() def test_muse_vmem_active(self): num = muse('Active') - self.assertAlmostEqual( - psutil.virtual_memory().active, num, delta=TOLERANCE_SYS_MEM - ) + assert abs(psutil.virtual_memory().active - num) < TOLERANCE_SYS_MEM - @unittest.skipIf(not MUSE_AVAILABLE, "muse not installed") + @pytest.mark.skipif(not MUSE_AVAILABLE, reason="muse not installed") @retry_on_failure() def test_muse_vmem_inactive(self): num = muse('Inactive') - self.assertAlmostEqual( - psutil.virtual_memory().inactive, num, delta=TOLERANCE_SYS_MEM - ) + assert abs(psutil.virtual_memory().inactive - num) < TOLERANCE_SYS_MEM - @unittest.skipIf(not MUSE_AVAILABLE, "muse not installed") + @pytest.mark.skipif(not MUSE_AVAILABLE, reason="muse not installed") @retry_on_failure() def test_muse_vmem_wired(self): num = muse('Wired') - self.assertAlmostEqual( - psutil.virtual_memory().wired, num, delta=TOLERANCE_SYS_MEM - ) + assert abs(psutil.virtual_memory().wired - num) < TOLERANCE_SYS_MEM - @unittest.skipIf(not MUSE_AVAILABLE, "muse not installed") + @pytest.mark.skipif(not MUSE_AVAILABLE, reason="muse not installed") @retry_on_failure() def test_muse_vmem_cached(self): num = muse('Cache') - self.assertAlmostEqual( - psutil.virtual_memory().cached, num, delta=TOLERANCE_SYS_MEM - ) + assert abs(psutil.virtual_memory().cached - num) < TOLERANCE_SYS_MEM - @unittest.skipIf(not MUSE_AVAILABLE, "muse not installed") + @pytest.mark.skipif(not MUSE_AVAILABLE, reason="muse not installed") @retry_on_failure() def test_muse_vmem_free(self): num = muse('Free') - self.assertAlmostEqual( - psutil.virtual_memory().free, num, delta=TOLERANCE_SYS_MEM - ) + assert abs(psutil.virtual_memory().free - num) < TOLERANCE_SYS_MEM - @unittest.skipIf(not MUSE_AVAILABLE, "muse not installed") + @pytest.mark.skipif(not MUSE_AVAILABLE, reason="muse not installed") @retry_on_failure() def test_muse_vmem_buffers(self): num = muse('Buffer') - self.assertAlmostEqual( - psutil.virtual_memory().buffers, num, delta=TOLERANCE_SYS_MEM - ) + assert abs(psutil.virtual_memory().buffers - num) < TOLERANCE_SYS_MEM def test_cpu_stats_ctx_switches(self): - self.assertAlmostEqual( - psutil.cpu_stats().ctx_switches, - sysctl('vm.stats.sys.v_swtch'), - delta=1000, + assert ( + abs( + psutil.cpu_stats().ctx_switches + - sysctl('vm.stats.sys.v_swtch') + ) + < 1000 ) def test_cpu_stats_interrupts(self): - self.assertAlmostEqual( - psutil.cpu_stats().interrupts, - sysctl('vm.stats.sys.v_intr'), - delta=1000, + assert ( + abs(psutil.cpu_stats().interrupts - sysctl('vm.stats.sys.v_intr')) + < 1000 ) def test_cpu_stats_soft_interrupts(self): - self.assertAlmostEqual( - psutil.cpu_stats().soft_interrupts, - sysctl('vm.stats.sys.v_soft'), - delta=1000, + assert ( + abs( + psutil.cpu_stats().soft_interrupts + - sysctl('vm.stats.sys.v_soft') + ) + < 1000 ) @retry_on_failure() def test_cpu_stats_syscalls(self): # pretty high tolerance but it looks like it's OK. - self.assertAlmostEqual( - psutil.cpu_stats().syscalls, - sysctl('vm.stats.sys.v_syscall'), - delta=200000, + assert ( + abs(psutil.cpu_stats().syscalls - sysctl('vm.stats.sys.v_syscall')) + < 200000 ) # def test_cpu_stats_traps(self): @@ -417,21 +394,15 @@ def test_cpu_stats_syscalls(self): def test_swapmem_free(self): _total, _used, free = self.parse_swapinfo() - self.assertAlmostEqual( - psutil.swap_memory().free, free, delta=TOLERANCE_SYS_MEM - ) + assert abs(psutil.swap_memory().free - free) < TOLERANCE_SYS_MEM def test_swapmem_used(self): _total, used, _free = self.parse_swapinfo() - self.assertAlmostEqual( - psutil.swap_memory().used, used, delta=TOLERANCE_SYS_MEM - ) + assert abs(psutil.swap_memory().used - used) < TOLERANCE_SYS_MEM def test_swapmem_total(self): total, _used, _free = self.parse_swapinfo() - self.assertAlmostEqual( - psutil.swap_memory().total, total, delta=TOLERANCE_SYS_MEM - ) + assert abs(psutil.swap_memory().total - total) < TOLERANCE_SYS_MEM # --- others @@ -440,11 +411,11 @@ def test_boot_time(self): s = s[s.find(" sec = ") + 7 :] s = s[: s.find(',')] btime = int(s) - self.assertEqual(btime, psutil.boot_time()) + assert btime == psutil.boot_time() # --- sensors_battery - @unittest.skipIf(not HAS_BATTERY, "no battery") + @pytest.mark.skipif(not HAS_BATTERY, reason="no battery") def test_sensors_battery(self): def secs2hours(secs): m, _s = divmod(secs, 60) @@ -458,37 +429,36 @@ def secs2hours(secs): metrics = psutil.sensors_battery() percent = int(fields['Remaining capacity:'].replace('%', '')) remaining_time = fields['Remaining time:'] - self.assertEqual(metrics.percent, percent) + assert metrics.percent == percent if remaining_time == 'unknown': - self.assertEqual(metrics.secsleft, psutil.POWER_TIME_UNLIMITED) + assert metrics.secsleft == psutil.POWER_TIME_UNLIMITED else: - self.assertEqual(secs2hours(metrics.secsleft), remaining_time) + assert secs2hours(metrics.secsleft) == remaining_time - @unittest.skipIf(not HAS_BATTERY, "no battery") + @pytest.mark.skipif(not HAS_BATTERY, reason="no battery") def test_sensors_battery_against_sysctl(self): - self.assertEqual( - psutil.sensors_battery().percent, sysctl("hw.acpi.battery.life") + assert psutil.sensors_battery().percent == sysctl( + "hw.acpi.battery.life" ) - self.assertEqual( - psutil.sensors_battery().power_plugged, - sysctl("hw.acpi.acline") == 1, + assert psutil.sensors_battery().power_plugged == ( + sysctl("hw.acpi.acline") == 1 ) secsleft = psutil.sensors_battery().secsleft if secsleft < 0: - self.assertEqual(sysctl("hw.acpi.battery.time"), -1) + assert sysctl("hw.acpi.battery.time") == -1 else: - self.assertEqual(secsleft, sysctl("hw.acpi.battery.time") * 60) + assert secsleft == sysctl("hw.acpi.battery.time") * 60 - @unittest.skipIf(HAS_BATTERY, "has battery") + @pytest.mark.skipif(HAS_BATTERY, reason="has battery") def test_sensors_battery_no_battery(self): # If no battery is present one of these calls is supposed # to fail, see: # https://github.com/giampaolo/psutil/issues/1074 - with self.assertRaises(RuntimeError): + with pytest.raises(RuntimeError): sysctl("hw.acpi.battery.life") sysctl("hw.acpi.battery.time") sysctl("hw.acpi.acline") - self.assertIsNone(psutil.sensors_battery()) + assert psutil.sensors_battery() is None # --- sensors_temperatures @@ -500,18 +470,20 @@ def test_sensors_temperatures_against_sysctl(self): try: sysctl_result = int(float(sysctl(sensor)[:-1])) except RuntimeError: - raise unittest.SkipTest("temperatures not supported by kernel") - self.assertAlmostEqual( - psutil.sensors_temperatures()["coretemp"][cpu].current, - sysctl_result, - delta=10, + raise pytest.skip("temperatures not supported by kernel") + assert ( + abs( + psutil.sensors_temperatures()["coretemp"][cpu].current + - sysctl_result + ) + < 10 ) sensor = "dev.cpu.%s.coretemp.tjmax" % cpu sysctl_result = int(float(sysctl(sensor)[:-1])) - self.assertEqual( - psutil.sensors_temperatures()["coretemp"][cpu].high, - sysctl_result, + assert ( + psutil.sensors_temperatures()["coretemp"][cpu].high + == sysctl_result ) @@ -520,13 +492,13 @@ def test_sensors_temperatures_against_sysctl(self): # ===================================================================== -@unittest.skipIf(not OPENBSD, "OPENBSD only") +@pytest.mark.skipif(not OPENBSD, reason="OPENBSD only") class OpenBSDTestCase(PsutilTestCase): def test_boot_time(self): s = sysctl('kern.boottime') sys_bt = datetime.datetime.strptime(s, "%a %b %d %H:%M:%S %Y") psutil_bt = datetime.datetime.fromtimestamp(psutil.boot_time()) - self.assertEqual(sys_bt, psutil_bt) + assert sys_bt == psutil_bt # ===================================================================== @@ -534,7 +506,7 @@ def test_boot_time(self): # ===================================================================== -@unittest.skipIf(not NETBSD, "NETBSD only") +@pytest.mark.skipif(not NETBSD, reason="NETBSD only") class NetBSDTestCase(PsutilTestCase): @staticmethod def parse_meminfo(look_for): @@ -547,57 +519,55 @@ def parse_meminfo(look_for): # --- virtual mem def test_vmem_total(self): - self.assertEqual( - psutil.virtual_memory().total, self.parse_meminfo("MemTotal:") - ) + assert psutil.virtual_memory().total == self.parse_meminfo("MemTotal:") def test_vmem_free(self): - self.assertAlmostEqual( - psutil.virtual_memory().free, - self.parse_meminfo("MemFree:"), - delta=TOLERANCE_SYS_MEM, + assert ( + abs(psutil.virtual_memory().free - self.parse_meminfo("MemFree:")) + < TOLERANCE_SYS_MEM ) def test_vmem_buffers(self): - self.assertAlmostEqual( - psutil.virtual_memory().buffers, - self.parse_meminfo("Buffers:"), - delta=TOLERANCE_SYS_MEM, + assert ( + abs( + psutil.virtual_memory().buffers + - self.parse_meminfo("Buffers:") + ) + < TOLERANCE_SYS_MEM ) def test_vmem_shared(self): - self.assertAlmostEqual( - psutil.virtual_memory().shared, - self.parse_meminfo("MemShared:"), - delta=TOLERANCE_SYS_MEM, + assert ( + abs( + psutil.virtual_memory().shared + - self.parse_meminfo("MemShared:") + ) + < TOLERANCE_SYS_MEM ) def test_vmem_cached(self): - self.assertAlmostEqual( - psutil.virtual_memory().cached, - self.parse_meminfo("Cached:"), - delta=TOLERANCE_SYS_MEM, + assert ( + abs(psutil.virtual_memory().cached - self.parse_meminfo("Cached:")) + < TOLERANCE_SYS_MEM ) # --- swap mem def test_swapmem_total(self): - self.assertAlmostEqual( - psutil.swap_memory().total, - self.parse_meminfo("SwapTotal:"), - delta=TOLERANCE_SYS_MEM, + assert ( + abs(psutil.swap_memory().total - self.parse_meminfo("SwapTotal:")) + < TOLERANCE_SYS_MEM ) def test_swapmem_free(self): - self.assertAlmostEqual( - psutil.swap_memory().free, - self.parse_meminfo("SwapFree:"), - delta=TOLERANCE_SYS_MEM, + assert ( + abs(psutil.swap_memory().free - self.parse_meminfo("SwapFree:")) + < TOLERANCE_SYS_MEM ) def test_swapmem_used(self): smem = psutil.swap_memory() - self.assertEqual(smem.used, smem.total - smem.free) + assert smem.used == smem.total - smem.free # --- others @@ -609,9 +579,7 @@ def test_cpu_stats_interrupts(self): break else: raise ValueError("couldn't find line") - self.assertAlmostEqual( - psutil.cpu_stats().interrupts, interrupts, delta=1000 - ) + assert abs(psutil.cpu_stats().interrupts - interrupts) < 1000 def test_cpu_stats_ctx_switches(self): with open('/proc/stat', 'rb') as f: @@ -621,12 +589,4 @@ def test_cpu_stats_ctx_switches(self): break else: raise ValueError("couldn't find line") - self.assertAlmostEqual( - psutil.cpu_stats().ctx_switches, ctx_switches, delta=1000 - ) - - -if __name__ == '__main__': - from psutil.tests.runner import run_from_name - - run_from_name(__file__) + assert abs(psutil.cpu_stats().ctx_switches - ctx_switches) < 1000 diff --git a/psutil/tests/test_connections.py b/psutil/tests/test_connections.py index 4a0674d62..c9256a17f 100755 --- a/psutil/tests/test_connections.py +++ b/psutil/tests/test_connections.py @@ -9,7 +9,6 @@ import os import socket import textwrap -import unittest from contextlib import closing from socket import AF_INET from socket import AF_INET6 @@ -36,9 +35,9 @@ from psutil.tests import check_connection_ntuple from psutil.tests import create_sockets from psutil.tests import filter_proc_net_connections +from psutil.tests import pytest from psutil.tests import reap_children from psutil.tests import retry_on_failure -from psutil.tests import serialrun from psutil.tests import skip_on_access_denied from psutil.tests import tcp_socketpair from psutil.tests import unix_socketpair @@ -55,14 +54,14 @@ def this_proc_net_connections(kind): return cons -@serialrun +@pytest.mark.xdist_group(name="serial") class ConnectionTestCase(PsutilTestCase): def setUp(self): - self.assertEqual(this_proc_net_connections(kind='all'), []) + assert this_proc_net_connections(kind='all') == [] def tearDown(self): # Make sure we closed all resources. - self.assertEqual(this_proc_net_connections(kind='all'), []) + assert this_proc_net_connections(kind='all') == [] def compare_procsys_connections(self, pid, proc_cons, kind='all'): """Given a process PID and its list of connections compare @@ -82,11 +81,11 @@ def compare_procsys_connections(self, pid, proc_cons, kind='all'): sys_cons = [c[:-1] for c in sys_cons if c.pid == pid] sys_cons.sort() proc_cons.sort() - self.assertEqual(proc_cons, sys_cons) + assert proc_cons == sys_cons class TestBasicOperations(ConnectionTestCase): - @unittest.skipIf(SKIP_SYSCONS, "requires root") + @pytest.mark.skipif(SKIP_SYSCONS, reason="requires root") def test_system(self): with create_sockets(): for conn in psutil.net_connections(kind='all'): @@ -98,11 +97,13 @@ def test_process(self): check_connection_ntuple(conn) def test_invalid_kind(self): - self.assertRaises(ValueError, this_proc_net_connections, kind='???') - self.assertRaises(ValueError, psutil.net_connections, kind='???') + with pytest.raises(ValueError): + this_proc_net_connections(kind='???') + with pytest.raises(ValueError): + psutil.net_connections(kind='???') -@serialrun +@pytest.mark.xdist_group(name="serial") class TestUnconnectedSockets(ConnectionTestCase): """Tests sockets which are open but not connected to anything.""" @@ -114,9 +115,9 @@ def get_conn_from_sock(self, sock): # so there may be more connections. return smap[sock.fileno()] else: - self.assertEqual(len(cons), 1) + assert len(cons) == 1 if cons[0].fd != -1: - self.assertEqual(smap[sock.fileno()].fd, sock.fileno()) + assert smap[sock.fileno()].fd == sock.fileno() return cons[0] def check_socket(self, sock): @@ -129,12 +130,10 @@ def check_socket(self, sock): # fd, family, type if conn.fd != -1: - self.assertEqual(conn.fd, sock.fileno()) - self.assertEqual(conn.family, sock.family) + assert conn.fd == sock.fileno() + assert conn.family == sock.family # see: http://bugs.python.org/issue30204 - self.assertEqual( - conn.type, sock.getsockopt(socket.SOL_SOCKET, socket.SO_TYPE) - ) + assert conn.type == sock.getsockopt(socket.SOL_SOCKET, socket.SO_TYPE) # local address laddr = sock.getsockname() @@ -143,7 +142,7 @@ def check_socket(self, sock): laddr = laddr.decode() if sock.family == AF_INET6: laddr = laddr[:2] - self.assertEqual(conn.laddr, laddr) + assert conn.laddr == laddr # XXX Solaris can't retrieve system-wide UNIX sockets if sock.family == AF_UNIX and HAS_NET_CONNECTIONS_UNIX: @@ -155,50 +154,50 @@ def test_tcp_v4(self): addr = ("127.0.0.1", 0) with closing(bind_socket(AF_INET, SOCK_STREAM, addr=addr)) as sock: conn = self.check_socket(sock) - self.assertEqual(conn.raddr, ()) - self.assertEqual(conn.status, psutil.CONN_LISTEN) + assert conn.raddr == () + assert conn.status == psutil.CONN_LISTEN - @unittest.skipIf(not supports_ipv6(), "IPv6 not supported") + @pytest.mark.skipif(not supports_ipv6(), reason="IPv6 not supported") def test_tcp_v6(self): addr = ("::1", 0) with closing(bind_socket(AF_INET6, SOCK_STREAM, addr=addr)) as sock: conn = self.check_socket(sock) - self.assertEqual(conn.raddr, ()) - self.assertEqual(conn.status, psutil.CONN_LISTEN) + assert conn.raddr == () + assert conn.status == psutil.CONN_LISTEN def test_udp_v4(self): addr = ("127.0.0.1", 0) with closing(bind_socket(AF_INET, SOCK_DGRAM, addr=addr)) as sock: conn = self.check_socket(sock) - self.assertEqual(conn.raddr, ()) - self.assertEqual(conn.status, psutil.CONN_NONE) + assert conn.raddr == () + assert conn.status == psutil.CONN_NONE - @unittest.skipIf(not supports_ipv6(), "IPv6 not supported") + @pytest.mark.skipif(not supports_ipv6(), reason="IPv6 not supported") def test_udp_v6(self): addr = ("::1", 0) with closing(bind_socket(AF_INET6, SOCK_DGRAM, addr=addr)) as sock: conn = self.check_socket(sock) - self.assertEqual(conn.raddr, ()) - self.assertEqual(conn.status, psutil.CONN_NONE) + assert conn.raddr == () + assert conn.status == psutil.CONN_NONE - @unittest.skipIf(not POSIX, 'POSIX only') + @pytest.mark.skipif(not POSIX, reason="POSIX only") def test_unix_tcp(self): testfn = self.get_testfn() with closing(bind_unix_socket(testfn, type=SOCK_STREAM)) as sock: conn = self.check_socket(sock) - self.assertEqual(conn.raddr, "") - self.assertEqual(conn.status, psutil.CONN_NONE) + assert conn.raddr == "" # noqa + assert conn.status == psutil.CONN_NONE - @unittest.skipIf(not POSIX, 'POSIX only') + @pytest.mark.skipif(not POSIX, reason="POSIX only") def test_unix_udp(self): testfn = self.get_testfn() with closing(bind_unix_socket(testfn, type=SOCK_STREAM)) as sock: conn = self.check_socket(sock) - self.assertEqual(conn.raddr, "") - self.assertEqual(conn.status, psutil.CONN_NONE) + assert conn.raddr == "" # noqa + assert conn.status == psutil.CONN_NONE -@serialrun +@pytest.mark.xdist_group(name="serial") class TestConnectedSocket(ConnectionTestCase): """Test socket pairs which are actually connected to each other. @@ -206,16 +205,16 @@ class TestConnectedSocket(ConnectionTestCase): # On SunOS, even after we close() it, the server socket stays around # in TIME_WAIT state. - @unittest.skipIf(SUNOS, "unreliable on SUONS") + @pytest.mark.skipif(SUNOS, reason="unreliable on SUONS") def test_tcp(self): addr = ("127.0.0.1", 0) - self.assertEqual(this_proc_net_connections(kind='tcp4'), []) + assert this_proc_net_connections(kind='tcp4') == [] server, client = tcp_socketpair(AF_INET, addr=addr) try: cons = this_proc_net_connections(kind='tcp4') - self.assertEqual(len(cons), 2) - self.assertEqual(cons[0].status, psutil.CONN_ESTABLISHED) - self.assertEqual(cons[1].status, psutil.CONN_ESTABLISHED) + assert len(cons) == 2 + assert cons[0].status == psutil.CONN_ESTABLISHED + assert cons[1].status == psutil.CONN_ESTABLISHED # May not be fast enough to change state so it stays # commenteed. # client.close() @@ -226,7 +225,7 @@ def test_tcp(self): server.close() client.close() - @unittest.skipIf(not POSIX, 'POSIX only') + @pytest.mark.skipif(not POSIX, reason="POSIX only") def test_unix(self): testfn = self.get_testfn() server, client = unix_socketpair(testfn) @@ -238,17 +237,17 @@ def test_unix(self): # On NetBSD creating a UNIX socket will cause # a UNIX connection to /var/run/log. cons = [c for c in cons if c.raddr != '/var/run/log'] - self.assertEqual(len(cons), 2, msg=cons) + assert len(cons) == 2 if LINUX or FREEBSD or SUNOS or OPENBSD: # remote path is never set - self.assertEqual(cons[0].raddr, "") - self.assertEqual(cons[1].raddr, "") + assert cons[0].raddr == "" # noqa + assert cons[1].raddr == "" # noqa # one local address should though - self.assertEqual(testfn, cons[0].laddr or cons[1].laddr) + assert testfn == (cons[0].laddr or cons[1].laddr) else: # On other systems either the laddr or raddr # of both peers are set. - self.assertEqual(cons[0].laddr or cons[1].laddr, testfn) + assert (cons[0].laddr or cons[1].laddr) == testfn finally: server.close() client.close() @@ -258,12 +257,12 @@ class TestFilters(ConnectionTestCase): def test_filters(self): def check(kind, families, types): for conn in this_proc_net_connections(kind=kind): - self.assertIn(conn.family, families) - self.assertIn(conn.type, types) + assert conn.family in families + assert conn.type in types if not SKIP_SYSCONS: for conn in psutil.net_connections(kind=kind): - self.assertIn(conn.family, families) - self.assertIn(conn.type, types) + assert conn.family in families + assert conn.type in types with create_sockets(): check( @@ -304,17 +303,17 @@ def check_conn(proc, conn, family, type, laddr, raddr, status, kinds): "udp6", ) check_connection_ntuple(conn) - self.assertEqual(conn.family, family) - self.assertEqual(conn.type, type) - self.assertEqual(conn.laddr, laddr) - self.assertEqual(conn.raddr, raddr) - self.assertEqual(conn.status, status) + assert conn.family == family + assert conn.type == type + assert conn.laddr == laddr + assert conn.raddr == raddr + assert conn.status == status for kind in all_kinds: cons = proc.net_connections(kind=kind) if kind in kinds: - self.assertNotEqual(cons, []) + assert cons != [] else: - self.assertEqual(cons, []) + assert cons == [] # compare against system-wide connections # XXX Solaris can't retrieve system-wide UNIX # sockets. @@ -374,7 +373,7 @@ def check_conn(proc, conn, family, type, laddr, raddr, status, kinds): for p in psutil.Process().children(): cons = p.net_connections() - self.assertEqual(len(cons), 1) + assert len(cons) == 1 for conn in cons: # TCP v4 if p.pid == tcp4_proc.pid: @@ -429,71 +428,71 @@ def test_count(self): with create_sockets(): # tcp cons = this_proc_net_connections(kind='tcp') - self.assertEqual(len(cons), 2 if supports_ipv6() else 1) + assert len(cons) == (2 if supports_ipv6() else 1) for conn in cons: - self.assertIn(conn.family, (AF_INET, AF_INET6)) - self.assertEqual(conn.type, SOCK_STREAM) + assert conn.family in (AF_INET, AF_INET6) + assert conn.type == SOCK_STREAM # tcp4 cons = this_proc_net_connections(kind='tcp4') - self.assertEqual(len(cons), 1) - self.assertEqual(cons[0].family, AF_INET) - self.assertEqual(cons[0].type, SOCK_STREAM) + assert len(cons) == 1 + assert cons[0].family == AF_INET + assert cons[0].type == SOCK_STREAM # tcp6 if supports_ipv6(): cons = this_proc_net_connections(kind='tcp6') - self.assertEqual(len(cons), 1) - self.assertEqual(cons[0].family, AF_INET6) - self.assertEqual(cons[0].type, SOCK_STREAM) + assert len(cons) == 1 + assert cons[0].family == AF_INET6 + assert cons[0].type == SOCK_STREAM # udp cons = this_proc_net_connections(kind='udp') - self.assertEqual(len(cons), 2 if supports_ipv6() else 1) + assert len(cons) == (2 if supports_ipv6() else 1) for conn in cons: - self.assertIn(conn.family, (AF_INET, AF_INET6)) - self.assertEqual(conn.type, SOCK_DGRAM) + assert conn.family in (AF_INET, AF_INET6) + assert conn.type == SOCK_DGRAM # udp4 cons = this_proc_net_connections(kind='udp4') - self.assertEqual(len(cons), 1) - self.assertEqual(cons[0].family, AF_INET) - self.assertEqual(cons[0].type, SOCK_DGRAM) + assert len(cons) == 1 + assert cons[0].family == AF_INET + assert cons[0].type == SOCK_DGRAM # udp6 if supports_ipv6(): cons = this_proc_net_connections(kind='udp6') - self.assertEqual(len(cons), 1) - self.assertEqual(cons[0].family, AF_INET6) - self.assertEqual(cons[0].type, SOCK_DGRAM) + assert len(cons) == 1 + assert cons[0].family == AF_INET6 + assert cons[0].type == SOCK_DGRAM # inet cons = this_proc_net_connections(kind='inet') - self.assertEqual(len(cons), 4 if supports_ipv6() else 2) + assert len(cons) == (4 if supports_ipv6() else 2) for conn in cons: - self.assertIn(conn.family, (AF_INET, AF_INET6)) - self.assertIn(conn.type, (SOCK_STREAM, SOCK_DGRAM)) + assert conn.family in (AF_INET, AF_INET6) + assert conn.type in (SOCK_STREAM, SOCK_DGRAM) # inet6 if supports_ipv6(): cons = this_proc_net_connections(kind='inet6') - self.assertEqual(len(cons), 2) + assert len(cons) == 2 for conn in cons: - self.assertEqual(conn.family, AF_INET6) - self.assertIn(conn.type, (SOCK_STREAM, SOCK_DGRAM)) + assert conn.family == AF_INET6 + assert conn.type in (SOCK_STREAM, SOCK_DGRAM) # Skipped on BSD becayse by default the Python process # creates a UNIX socket to '/var/run/log'. if HAS_NET_CONNECTIONS_UNIX and not (FREEBSD or NETBSD): cons = this_proc_net_connections(kind='unix') - self.assertEqual(len(cons), 3) + assert len(cons) == 3 for conn in cons: - self.assertEqual(conn.family, AF_UNIX) - self.assertIn(conn.type, (SOCK_STREAM, SOCK_DGRAM)) + assert conn.family == AF_UNIX + assert conn.type in (SOCK_STREAM, SOCK_DGRAM) -@unittest.skipIf(SKIP_SYSCONS, "requires root") +@pytest.mark.skipif(SKIP_SYSCONS, reason="requires root") class TestSystemWideConnections(ConnectionTestCase): """Tests for net_connections().""" def test_it(self): def check(cons, families, types_): for conn in cons: - self.assertIn(conn.family, families, msg=conn) + assert conn.family in families if conn.family != AF_UNIX: - self.assertIn(conn.type, types_, msg=conn) + assert conn.type in types_ check_connection_ntuple(conn) with create_sockets(): @@ -505,7 +504,7 @@ def check(cons, families, types_): continue families, types_ = groups cons = psutil.net_connections(kind) - self.assertEqual(len(cons), len(set(cons))) + assert len(cons) == len(set(cons)) check(cons, families, types_) @retry_on_failure() @@ -543,11 +542,9 @@ def test_multi_sockets_procs(self): x for x in psutil.net_connections(kind='all') if x.pid in pids ] for pid in pids: - self.assertEqual( - len([x for x in syscons if x.pid == pid]), expected - ) + assert len([x for x in syscons if x.pid == pid]) == expected p = psutil.Process(pid) - self.assertEqual(len(p.net_connections('all')), expected) + assert len(p.net_connections('all')) == expected class TestMisc(PsutilTestCase): @@ -559,8 +556,8 @@ def test_net_connection_constants(self): num = getattr(psutil, name) str_ = str(num) assert str_.isupper(), str_ - self.assertNotIn(str, strs) - self.assertNotIn(num, ints) + assert str not in strs + assert num not in ints ints.append(num) strs.append(str_) if SUNOS: @@ -568,9 +565,3 @@ def test_net_connection_constants(self): psutil.CONN_BOUND # noqa if WINDOWS: psutil.CONN_DELETE_TCB # noqa - - -if __name__ == '__main__': - from psutil.tests.runner import run_from_name - - run_from_name(__file__) diff --git a/psutil/tests/test_contracts.py b/psutil/tests/test_contracts.py index a5469ac8a..c0ec6a8f7 100755 --- a/psutil/tests/test_contracts.py +++ b/psutil/tests/test_contracts.py @@ -11,7 +11,6 @@ import platform import signal -import unittest import psutil from psutil import AIX @@ -37,6 +36,7 @@ from psutil.tests import enum from psutil.tests import is_namedtuple from psutil.tests import kernel_version +from psutil.tests import pytest # =================================================================== @@ -49,7 +49,7 @@ class TestAvailConstantsAPIs(PsutilTestCase): def test_PROCFS_PATH(self): - self.assertEqual(hasattr(psutil, "PROCFS_PATH"), LINUX or SUNOS or AIX) + assert hasattr(psutil, "PROCFS_PATH") == (LINUX or SUNOS or AIX) def test_win_priority(self): ae = self.assertEqual @@ -74,8 +74,9 @@ def test_linux_ioprio_windows(self): ae(hasattr(psutil, "IOPRIO_LOW"), WINDOWS) ae(hasattr(psutil, "IOPRIO_VERYLOW"), WINDOWS) - @unittest.skipIf( - GITHUB_ACTIONS and LINUX, "unsupported on GITHUB_ACTIONS + LINUX" + @pytest.mark.skipif( + GITHUB_ACTIONS and LINUX, + reason="unsupported on GITHUB_ACTIONS + LINUX", ) def test_rlimit(self): ae = self.assertEqual @@ -111,36 +112,31 @@ def test_rlimit(self): class TestAvailSystemAPIs(PsutilTestCase): def test_win_service_iter(self): - self.assertEqual(hasattr(psutil, "win_service_iter"), WINDOWS) + assert hasattr(psutil, "win_service_iter") == WINDOWS def test_win_service_get(self): - self.assertEqual(hasattr(psutil, "win_service_get"), WINDOWS) + assert hasattr(psutil, "win_service_get") == WINDOWS def test_cpu_freq(self): - self.assertEqual( - hasattr(psutil, "cpu_freq"), - LINUX or MACOS or WINDOWS or FREEBSD or OPENBSD, + assert hasattr(psutil, "cpu_freq") == ( + LINUX or MACOS or WINDOWS or FREEBSD or OPENBSD ) def test_sensors_temperatures(self): - self.assertEqual( - hasattr(psutil, "sensors_temperatures"), LINUX or FREEBSD - ) + assert hasattr(psutil, "sensors_temperatures") == (LINUX or FREEBSD) def test_sensors_fans(self): - self.assertEqual(hasattr(psutil, "sensors_fans"), LINUX) + assert hasattr(psutil, "sensors_fans") == LINUX def test_battery(self): - self.assertEqual( - hasattr(psutil, "sensors_battery"), - LINUX or WINDOWS or FREEBSD or MACOS, + assert hasattr(psutil, "sensors_battery") == ( + LINUX or WINDOWS or FREEBSD or MACOS ) class TestAvailProcessAPIs(PsutilTestCase): def test_environ(self): - self.assertEqual( - hasattr(psutil.Process, "environ"), + assert hasattr(psutil.Process, "environ") == ( LINUX or MACOS or WINDOWS @@ -148,51 +144,51 @@ def test_environ(self): or SUNOS or FREEBSD or OPENBSD - or NETBSD, + or NETBSD ) def test_uids(self): - self.assertEqual(hasattr(psutil.Process, "uids"), POSIX) + assert hasattr(psutil.Process, "uids") == POSIX def test_gids(self): - self.assertEqual(hasattr(psutil.Process, "uids"), POSIX) + assert hasattr(psutil.Process, "uids") == POSIX def test_terminal(self): - self.assertEqual(hasattr(psutil.Process, "terminal"), POSIX) + assert hasattr(psutil.Process, "terminal") == POSIX def test_ionice(self): - self.assertEqual(hasattr(psutil.Process, "ionice"), LINUX or WINDOWS) + assert hasattr(psutil.Process, "ionice") == (LINUX or WINDOWS) - @unittest.skipIf( - GITHUB_ACTIONS and LINUX, "unsupported on GITHUB_ACTIONS + LINUX" + @pytest.mark.skipif( + GITHUB_ACTIONS and LINUX, + reason="unsupported on GITHUB_ACTIONS + LINUX", ) def test_rlimit(self): - self.assertEqual(hasattr(psutil.Process, "rlimit"), LINUX or FREEBSD) + assert hasattr(psutil.Process, "rlimit") == (LINUX or FREEBSD) def test_io_counters(self): hasit = hasattr(psutil.Process, "io_counters") - self.assertEqual(hasit, not (MACOS or SUNOS)) + assert hasit == (not (MACOS or SUNOS)) def test_num_fds(self): - self.assertEqual(hasattr(psutil.Process, "num_fds"), POSIX) + assert hasattr(psutil.Process, "num_fds") == POSIX def test_num_handles(self): - self.assertEqual(hasattr(psutil.Process, "num_handles"), WINDOWS) + assert hasattr(psutil.Process, "num_handles") == WINDOWS def test_cpu_affinity(self): - self.assertEqual( - hasattr(psutil.Process, "cpu_affinity"), - LINUX or WINDOWS or FREEBSD, + assert hasattr(psutil.Process, "cpu_affinity") == ( + LINUX or WINDOWS or FREEBSD ) def test_cpu_num(self): - self.assertEqual( - hasattr(psutil.Process, "cpu_num"), LINUX or FREEBSD or SUNOS + assert hasattr(psutil.Process, "cpu_num") == ( + LINUX or FREEBSD or SUNOS ) def test_memory_maps(self): hasit = hasattr(psutil.Process, "memory_maps") - self.assertEqual(hasit, not (OPENBSD or NETBSD or AIX or MACOS)) + assert hasit == (not (OPENBSD or NETBSD or AIX or MACOS)) # =================================================================== @@ -213,9 +209,9 @@ def setUpClass(cls): def assert_ntuple_of_nums(self, nt, type_=float, gezero=True): assert is_namedtuple(nt) for n in nt: - self.assertIsInstance(n, type_) + assert isinstance(n, type_) if gezero: - self.assertGreaterEqual(n, 0) + assert n >= 0 def test_cpu_times(self): self.assert_ntuple_of_nums(psutil.cpu_times()) @@ -223,127 +219,121 @@ def test_cpu_times(self): self.assert_ntuple_of_nums(nt) def test_cpu_percent(self): - self.assertIsInstance(psutil.cpu_percent(interval=None), float) - self.assertIsInstance(psutil.cpu_percent(interval=0.00001), float) + assert isinstance(psutil.cpu_percent(interval=None), float) + assert isinstance(psutil.cpu_percent(interval=0.00001), float) def test_cpu_times_percent(self): self.assert_ntuple_of_nums(psutil.cpu_times_percent(interval=None)) self.assert_ntuple_of_nums(psutil.cpu_times_percent(interval=0.0001)) def test_cpu_count(self): - self.assertIsInstance(psutil.cpu_count(), int) + assert isinstance(psutil.cpu_count(), int) # TODO: remove this once 1892 is fixed - @unittest.skipIf( - MACOS and platform.machine() == 'arm64', "skipped due to #1892" + @pytest.mark.skipif( + MACOS and platform.machine() == 'arm64', reason="skipped due to #1892" ) - @unittest.skipIf(not HAS_CPU_FREQ, "not supported") + @pytest.mark.skipif(not HAS_CPU_FREQ, reason="not supported") def test_cpu_freq(self): if psutil.cpu_freq() is None: - raise unittest.SkipTest("cpu_freq() returns None") + raise pytest.skip("cpu_freq() returns None") self.assert_ntuple_of_nums(psutil.cpu_freq(), type_=(float, int, long)) 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(): - self.assertIsInstance(k, str) + assert isinstance(k, str) self.assert_ntuple_of_nums(v, type_=(int, long)) def test_disk_partitions(self): # Duplicate of test_system.py. Keep it anyway. for disk in psutil.disk_partitions(): - self.assertIsInstance(disk.device, str) - self.assertIsInstance(disk.mountpoint, str) - self.assertIsInstance(disk.fstype, str) - self.assertIsInstance(disk.opts, str) + assert isinstance(disk.device, str) + assert isinstance(disk.mountpoint, str) + assert isinstance(disk.fstype, str) + assert isinstance(disk.opts, str) - @unittest.skipIf(SKIP_SYSCONS, "requires root") + @pytest.mark.skipif(SKIP_SYSCONS, reason="requires root") def test_net_connections(self): with create_sockets(): ret = psutil.net_connections('all') - self.assertEqual(len(ret), len(set(ret))) + assert len(ret) == len(set(ret)) for conn in ret: assert is_namedtuple(conn) def test_net_if_addrs(self): # Duplicate of test_system.py. Keep it anyway. for ifname, addrs in psutil.net_if_addrs().items(): - self.assertIsInstance(ifname, str) + assert isinstance(ifname, str) for addr in addrs: if enum is not None and not PYPY: - self.assertIsInstance(addr.family, enum.IntEnum) + assert isinstance(addr.family, enum.IntEnum) else: - self.assertIsInstance(addr.family, int) - self.assertIsInstance(addr.address, str) - self.assertIsInstance(addr.netmask, (str, type(None))) - self.assertIsInstance(addr.broadcast, (str, type(None))) + assert isinstance(addr.family, int) + assert isinstance(addr.address, str) + assert isinstance(addr.netmask, (str, type(None))) + assert isinstance(addr.broadcast, (str, type(None))) - @unittest.skipIf(QEMU_USER, 'QEMU user not supported') + @pytest.mark.skipif(QEMU_USER, reason="QEMU user not supported") def test_net_if_stats(self): # Duplicate of test_system.py. Keep it anyway. for ifname, info in psutil.net_if_stats().items(): - self.assertIsInstance(ifname, str) - self.assertIsInstance(info.isup, bool) + assert isinstance(ifname, str) + assert isinstance(info.isup, bool) if enum is not None: - self.assertIsInstance(info.duplex, enum.IntEnum) + assert isinstance(info.duplex, enum.IntEnum) else: - self.assertIsInstance(info.duplex, int) - self.assertIsInstance(info.speed, int) - self.assertIsInstance(info.mtu, int) + assert isinstance(info.duplex, int) + assert isinstance(info.speed, int) + assert isinstance(info.mtu, int) - @unittest.skipIf(not HAS_NET_IO_COUNTERS, 'not supported') + @pytest.mark.skipif(not HAS_NET_IO_COUNTERS, reason="not supported") def test_net_io_counters(self): # Duplicate of test_system.py. Keep it anyway. for ifname in psutil.net_io_counters(pernic=True): - self.assertIsInstance(ifname, str) + assert isinstance(ifname, str) - @unittest.skipIf(not HAS_SENSORS_FANS, "not supported") + @pytest.mark.skipif(not HAS_SENSORS_FANS, reason="not supported") def test_sensors_fans(self): # Duplicate of test_system.py. Keep it anyway. for name, units in psutil.sensors_fans().items(): - self.assertIsInstance(name, str) + assert isinstance(name, str) for unit in units: - self.assertIsInstance(unit.label, str) - self.assertIsInstance(unit.current, (float, int, type(None))) + assert isinstance(unit.label, str) + assert isinstance(unit.current, (float, int, type(None))) - @unittest.skipIf(not HAS_SENSORS_TEMPERATURES, "not supported") + @pytest.mark.skipif(not HAS_SENSORS_TEMPERATURES, reason="not supported") def test_sensors_temperatures(self): # Duplicate of test_system.py. Keep it anyway. for name, units in psutil.sensors_temperatures().items(): - self.assertIsInstance(name, str) + assert isinstance(name, str) for unit in units: - self.assertIsInstance(unit.label, str) - self.assertIsInstance(unit.current, (float, int, type(None))) - self.assertIsInstance(unit.high, (float, int, type(None))) - self.assertIsInstance(unit.critical, (float, int, type(None))) + assert isinstance(unit.label, str) + assert isinstance(unit.current, (float, int, type(None))) + assert isinstance(unit.high, (float, int, type(None))) + assert isinstance(unit.critical, (float, int, type(None))) def test_boot_time(self): # Duplicate of test_system.py. Keep it anyway. - self.assertIsInstance(psutil.boot_time(), float) + assert isinstance(psutil.boot_time(), float) def test_users(self): # Duplicate of test_system.py. Keep it anyway. for user in psutil.users(): - self.assertIsInstance(user.name, str) - self.assertIsInstance(user.terminal, (str, type(None))) - self.assertIsInstance(user.host, (str, type(None))) - self.assertIsInstance(user.pid, (int, type(None))) + assert isinstance(user.name, str) + assert isinstance(user.terminal, (str, type(None))) + assert isinstance(user.host, (str, type(None))) + assert isinstance(user.pid, (int, type(None))) class TestProcessWaitType(PsutilTestCase): - @unittest.skipIf(not POSIX, "not POSIX") + @pytest.mark.skipif(not POSIX, reason="not POSIX") def test_negative_signal(self): p = psutil.Process(self.spawn_testproc().pid) p.terminate() code = p.wait() - self.assertEqual(code, -signal.SIGTERM) + assert code == -signal.SIGTERM if enum is not None: - self.assertIsInstance(code, enum.IntEnum) + assert isinstance(code, enum.IntEnum) else: - self.assertIsInstance(code, int) - - -if __name__ == '__main__': - from psutil.tests.runner import run_from_name - - run_from_name(__file__) + assert isinstance(code, int) diff --git a/psutil/tests/test_linux.py b/psutil/tests/test_linux.py index d8faac968..cdcb468e1 100755 --- a/psutil/tests/test_linux.py +++ b/psutil/tests/test_linux.py @@ -20,7 +20,6 @@ import struct import textwrap import time -import unittest import warnings import psutil @@ -36,6 +35,7 @@ from psutil.tests import HAS_GETLOADAVG from psutil.tests import HAS_RLIMIT from psutil.tests import PYPY +from psutil.tests import PYTEST_PARALLEL from psutil.tests import QEMU_USER from psutil.tests import TOLERANCE_DISK_USAGE from psutil.tests import TOLERANCE_SYS_MEM @@ -43,6 +43,7 @@ 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 @@ -214,7 +215,7 @@ def vmstat(stat): def get_free_version_info(): out = sh(["free", "-V"]).strip() if 'UNKNOWN' in out: - raise unittest.SkipTest("can't determine free version") + raise pytest.skip("can't determine free version") return tuple(map(int, re.findall(r'\d+', out.split()[-1]))) @@ -266,12 +267,12 @@ def open_mock(name, *args, **kwargs): # ===================================================================== -@unittest.skipIf(not LINUX, "LINUX only") +@pytest.mark.skipif(not LINUX, reason="LINUX only") class TestSystemVirtualMemoryAgainstFree(PsutilTestCase): def test_total(self): cli_value = free_physmem().total psutil_value = psutil.virtual_memory().total - self.assertEqual(cli_value, psutil_value) + assert cli_value == psutil_value @retry_on_failure() def test_used(self): @@ -284,36 +285,29 @@ def test_used(self): # https://gitlab.com/procps-ng/procps/commit/ # 2184e90d2e7cdb582f9a5b706b47015e56707e4d if get_free_version_info() < (3, 3, 12): - raise unittest.SkipTest("free version too old") + raise pytest.skip("free version too old") if get_free_version_info() >= (4, 0, 0): - raise unittest.SkipTest("free version too recent") + raise pytest.skip("free version too recent") cli_value = free_physmem().used psutil_value = psutil.virtual_memory().used - self.assertAlmostEqual( - cli_value, psutil_value, delta=TOLERANCE_SYS_MEM - ) + assert abs(cli_value - psutil_value) < TOLERANCE_SYS_MEM @retry_on_failure() def test_free(self): cli_value = free_physmem().free psutil_value = psutil.virtual_memory().free - self.assertAlmostEqual( - cli_value, psutil_value, delta=TOLERANCE_SYS_MEM - ) + assert abs(cli_value - psutil_value) < TOLERANCE_SYS_MEM @retry_on_failure() def test_shared(self): free = free_physmem() free_value = free.shared if free_value == 0: - raise unittest.SkipTest("free does not support 'shared' column") + raise pytest.skip("free does not support 'shared' column") psutil_value = psutil.virtual_memory().shared - self.assertAlmostEqual( - free_value, - psutil_value, - delta=TOLERANCE_SYS_MEM, - msg='%s %s \n%s' % (free_value, psutil_value, free.output), - ) + assert ( + abs(free_value - psutil_value) < TOLERANCE_SYS_MEM + ), '%s %s \n%s' % (free_value, psutil_value, free.output) @retry_on_failure() def test_available(self): @@ -322,26 +316,21 @@ def test_available(self): out = sh(["free", "-b"]) lines = out.split('\n') if 'available' not in lines[0]: - raise unittest.SkipTest("free does not support 'available' column") + raise pytest.skip("free does not support 'available' column") else: free_value = int(lines[1].split()[-1]) psutil_value = psutil.virtual_memory().available - self.assertAlmostEqual( - free_value, - psutil_value, - delta=TOLERANCE_SYS_MEM, - msg='%s %s \n%s' % (free_value, psutil_value, out), - ) + assert ( + abs(free_value - psutil_value) < TOLERANCE_SYS_MEM + ), '%s %s \n%s' % (free_value, psutil_value, out) -@unittest.skipIf(not LINUX, "LINUX only") +@pytest.mark.skipif(not LINUX, reason="LINUX only") class TestSystemVirtualMemoryAgainstVmstat(PsutilTestCase): def test_total(self): vmstat_value = vmstat('total memory') * 1024 psutil_value = psutil.virtual_memory().total - self.assertAlmostEqual( - vmstat_value, psutil_value, delta=TOLERANCE_SYS_MEM - ) + assert abs(vmstat_value - psutil_value) < TOLERANCE_SYS_MEM @retry_on_failure() def test_used(self): @@ -354,49 +343,39 @@ def test_used(self): # https://gitlab.com/procps-ng/procps/commit/ # 2184e90d2e7cdb582f9a5b706b47015e56707e4d if get_free_version_info() < (3, 3, 12): - raise unittest.SkipTest("free version too old") + raise pytest.skip("free version too old") if get_free_version_info() >= (4, 0, 0): - raise unittest.SkipTest("free version too recent") + raise pytest.skip("free version too recent") vmstat_value = vmstat('used memory') * 1024 psutil_value = psutil.virtual_memory().used - self.assertAlmostEqual( - vmstat_value, psutil_value, delta=TOLERANCE_SYS_MEM - ) + assert abs(vmstat_value - psutil_value) < TOLERANCE_SYS_MEM @retry_on_failure() def test_free(self): vmstat_value = vmstat('free memory') * 1024 psutil_value = psutil.virtual_memory().free - self.assertAlmostEqual( - vmstat_value, psutil_value, delta=TOLERANCE_SYS_MEM - ) + assert abs(vmstat_value - psutil_value) < TOLERANCE_SYS_MEM @retry_on_failure() def test_buffers(self): vmstat_value = vmstat('buffer memory') * 1024 psutil_value = psutil.virtual_memory().buffers - self.assertAlmostEqual( - vmstat_value, psutil_value, delta=TOLERANCE_SYS_MEM - ) + assert abs(vmstat_value - psutil_value) < TOLERANCE_SYS_MEM @retry_on_failure() def test_active(self): vmstat_value = vmstat('active memory') * 1024 psutil_value = psutil.virtual_memory().active - self.assertAlmostEqual( - vmstat_value, psutil_value, delta=TOLERANCE_SYS_MEM - ) + assert abs(vmstat_value - psutil_value) < TOLERANCE_SYS_MEM @retry_on_failure() def test_inactive(self): vmstat_value = vmstat('inactive memory') * 1024 psutil_value = psutil.virtual_memory().inactive - self.assertAlmostEqual( - vmstat_value, psutil_value, delta=TOLERANCE_SYS_MEM - ) + assert abs(vmstat_value - psutil_value) < TOLERANCE_SYS_MEM -@unittest.skipIf(not LINUX, "LINUX only") +@pytest.mark.skipif(not LINUX, reason="LINUX only") class TestSystemVirtualMemoryMocks(PsutilTestCase): def test_warnings_on_misses(self): # Emulate a case where /proc/meminfo provides few info. @@ -417,24 +396,22 @@ def test_warnings_on_misses(self): warnings.simplefilter("always") ret = psutil.virtual_memory() assert m.called - self.assertEqual(len(ws), 1) + assert len(ws) == 1 w = ws[0] - self.assertIn( - "memory stats couldn't be determined", str(w.message) - ) - self.assertIn("cached", str(w.message)) - self.assertIn("shared", str(w.message)) - self.assertIn("active", str(w.message)) - self.assertIn("inactive", str(w.message)) - self.assertIn("buffers", str(w.message)) - self.assertIn("available", str(w.message)) - self.assertEqual(ret.cached, 0) - self.assertEqual(ret.active, 0) - self.assertEqual(ret.inactive, 0) - self.assertEqual(ret.shared, 0) - self.assertEqual(ret.buffers, 0) - self.assertEqual(ret.available, 0) - self.assertEqual(ret.slab, 0) + assert "memory stats couldn't be determined" in str(w.message) + assert "cached" in str(w.message) + assert "shared" in str(w.message) + assert "active" in str(w.message) + assert "inactive" in str(w.message) + assert "buffers" in str(w.message) + assert "available" in str(w.message) + assert ret.cached == 0 + assert ret.active == 0 + assert ret.inactive == 0 + assert ret.shared == 0 + assert ret.buffers == 0 + assert ret.available == 0 + assert ret.slab == 0 @retry_on_failure() def test_avail_old_percent(self): @@ -450,7 +427,7 @@ def test_avail_old_percent(self): if b'MemAvailable:' in mems: b = mems[b'MemAvailable:'] diff_percent = abs(a - b) / a * 100 - self.assertLess(diff_percent, 15) + assert diff_percent < 15 def test_avail_old_comes_from_kernel(self): # Make sure "MemAvailable:" coluimn is used instead of relying @@ -474,10 +451,10 @@ def test_avail_old_comes_from_kernel(self): with warnings.catch_warnings(record=True) as ws: ret = psutil.virtual_memory() assert m.called - self.assertEqual(ret.available, 6574984 * 1024) + assert ret.available == 6574984 * 1024 w = ws[0] - self.assertIn( - "inactive memory stats couldn't be determined", str(w.message) + assert "inactive memory stats couldn't be determined" in str( + w.message ) def test_avail_old_missing_fields(self): @@ -499,10 +476,10 @@ def test_avail_old_missing_fields(self): with warnings.catch_warnings(record=True) as ws: ret = psutil.virtual_memory() assert m.called - self.assertEqual(ret.available, 2057400 * 1024 + 4818144 * 1024) + assert ret.available == 2057400 * 1024 + 4818144 * 1024 w = ws[0] - self.assertIn( - "inactive memory stats couldn't be determined", str(w.message) + assert "inactive memory stats couldn't be determined" in str( + w.message ) def test_avail_old_missing_zoneinfo(self): @@ -529,13 +506,11 @@ def test_avail_old_missing_zoneinfo(self): ): with warnings.catch_warnings(record=True) as ws: ret = psutil.virtual_memory() - self.assertEqual( - ret.available, 2057400 * 1024 + 4818144 * 1024 - ) + assert ret.available == 2057400 * 1024 + 4818144 * 1024 w = ws[0] - self.assertIn( - "inactive memory stats couldn't be determined", - str(w.message), + assert ( + "inactive memory stats couldn't be determined" + in str(w.message) ) def test_virtual_memory_mocked(self): @@ -593,16 +568,16 @@ def test_virtual_memory_mocked(self): with mock_open_content({"/proc/meminfo": content}) as m: mem = psutil.virtual_memory() assert m.called - self.assertEqual(mem.total, 100 * 1024) - self.assertEqual(mem.free, 2 * 1024) - self.assertEqual(mem.buffers, 4 * 1024) + assert mem.total == 100 * 1024 + assert mem.free == 2 * 1024 + assert mem.buffers == 4 * 1024 # cached mem also includes reclaimable memory - self.assertEqual(mem.cached, (5 + 23) * 1024) - self.assertEqual(mem.shared, 21 * 1024) - self.assertEqual(mem.active, 7 * 1024) - self.assertEqual(mem.inactive, 8 * 1024) - self.assertEqual(mem.slab, 22 * 1024) - self.assertEqual(mem.available, 3 * 1024) + assert mem.cached == (5 + 23) * 1024 + assert mem.shared == 21 * 1024 + assert mem.active == 7 * 1024 + assert mem.inactive == 8 * 1024 + assert mem.slab == 22 * 1024 + assert mem.available == 3 * 1024 # ===================================================================== @@ -610,7 +585,7 @@ def test_virtual_memory_mocked(self): # ===================================================================== -@unittest.skipIf(not LINUX, "LINUX only") +@pytest.mark.skipif(not LINUX, reason="LINUX only") class TestSystemSwapMemory(PsutilTestCase): @staticmethod def meminfo_has_swap_info(): @@ -622,25 +597,19 @@ def meminfo_has_swap_info(): def test_total(self): free_value = free_swap().total psutil_value = psutil.swap_memory().total - return self.assertAlmostEqual( - free_value, psutil_value, delta=TOLERANCE_SYS_MEM - ) + assert abs(free_value - psutil_value) < TOLERANCE_SYS_MEM @retry_on_failure() def test_used(self): free_value = free_swap().used psutil_value = psutil.swap_memory().used - return self.assertAlmostEqual( - free_value, psutil_value, delta=TOLERANCE_SYS_MEM - ) + assert abs(free_value - psutil_value) < TOLERANCE_SYS_MEM @retry_on_failure() def test_free(self): free_value = free_swap().free psutil_value = psutil.swap_memory().free - return self.assertAlmostEqual( - free_value, psutil_value, delta=TOLERANCE_SYS_MEM - ) + assert abs(free_value - psutil_value) < TOLERANCE_SYS_MEM def test_missing_sin_sout(self): with mock.patch('psutil._common.open', create=True) as m: @@ -648,15 +617,14 @@ def test_missing_sin_sout(self): warnings.simplefilter("always") ret = psutil.swap_memory() assert m.called - self.assertEqual(len(ws), 1) + assert len(ws) == 1 w = ws[0] - self.assertIn( - "'sin' and 'sout' swap memory stats couldn't " - "be determined", - str(w.message), + assert ( + "'sin' and 'sout' swap memory stats couldn't be determined" + in str(w.message) ) - self.assertEqual(ret.sin, 0) - self.assertEqual(ret.sout, 0) + assert ret.sin == 0 + assert ret.sout == 0 def test_no_vmstat_mocked(self): # see https://github.com/giampaolo/psutil/issues/722 @@ -667,22 +635,22 @@ def test_no_vmstat_mocked(self): warnings.simplefilter("always") ret = psutil.swap_memory() assert m.called - self.assertEqual(len(ws), 1) + assert len(ws) == 1 w = ws[0] - self.assertIn( + assert ( "'sin' and 'sout' swap memory stats couldn't " - "be determined and were set to 0", - str(w.message), + "be determined and were set to 0" + in str(w.message) ) - self.assertEqual(ret.sin, 0) - self.assertEqual(ret.sout, 0) + assert ret.sin == 0 + assert ret.sout == 0 def test_meminfo_against_sysinfo(self): # Make sure the content of /proc/meminfo about swap memory # matches sysinfo() syscall, see: # https://github.com/giampaolo/psutil/issues/1015 if not self.meminfo_has_swap_info(): - return unittest.skip("/proc/meminfo has no swap metrics") + raise pytest.skip("/proc/meminfo has no swap metrics") with mock.patch('psutil._pslinux.cext.linux_sysinfo') as m: swap = psutil.swap_memory() assert not m.called @@ -691,8 +659,8 @@ def test_meminfo_against_sysinfo(self): _, _, _, _, total, free, unit_multiplier = cext.linux_sysinfo() total *= unit_multiplier free *= unit_multiplier - self.assertEqual(swap.total, total) - self.assertAlmostEqual(swap.free, free, delta=TOLERANCE_SYS_MEM) + assert swap.total == total + assert abs(swap.free - free) < TOLERANCE_SYS_MEM def test_emulate_meminfo_has_no_metrics(self): # Emulate a case where /proc/meminfo provides no swap metrics @@ -708,58 +676,62 @@ def test_emulate_meminfo_has_no_metrics(self): # ===================================================================== -@unittest.skipIf(not LINUX, "LINUX only") +@pytest.mark.skipif(not LINUX, reason="LINUX only") class TestSystemCPUTimes(PsutilTestCase): def test_fields(self): fields = psutil.cpu_times()._fields kernel_ver = re.findall(r'\d+\.\d+\.\d+', os.uname()[2])[0] kernel_ver_info = tuple(map(int, kernel_ver.split('.'))) if kernel_ver_info >= (2, 6, 11): - self.assertIn('steal', fields) + assert 'steal' in fields else: - self.assertNotIn('steal', fields) + assert 'steal' not in fields if kernel_ver_info >= (2, 6, 24): - self.assertIn('guest', fields) + assert 'guest' in fields else: - self.assertNotIn('guest', fields) + assert 'guest' not in fields if kernel_ver_info >= (3, 2, 0): - self.assertIn('guest_nice', fields) + assert 'guest_nice' in fields else: - self.assertNotIn('guest_nice', fields) + assert 'guest_nice' not in fields -@unittest.skipIf(not LINUX, "LINUX only") +@pytest.mark.skipif(not LINUX, reason="LINUX only") class TestSystemCPUCountLogical(PsutilTestCase): - @unittest.skipIf( + @pytest.mark.skipif( not os.path.exists("/sys/devices/system/cpu/online"), - "/sys/devices/system/cpu/online does not exist", + reason="/sys/devices/system/cpu/online does not exist", ) def test_against_sysdev_cpu_online(self): with open("/sys/devices/system/cpu/online") as f: value = f.read().strip() if "-" in str(value): value = int(value.split('-')[1]) + 1 - self.assertEqual(psutil.cpu_count(), value) + assert psutil.cpu_count() == value - @unittest.skipIf( + @pytest.mark.skipif( not os.path.exists("/sys/devices/system/cpu"), - "/sys/devices/system/cpu does not exist", + reason="/sys/devices/system/cpu does not exist", ) def test_against_sysdev_cpu_num(self): ls = os.listdir("/sys/devices/system/cpu") count = len([x for x in ls if re.search(r"cpu\d+$", x) is not None]) - self.assertEqual(psutil.cpu_count(), count) + assert psutil.cpu_count() == count - @unittest.skipIf(not which("nproc"), "nproc utility not available") + @pytest.mark.skipif( + not which("nproc"), reason="nproc utility not available" + ) def test_against_nproc(self): num = int(sh("nproc --all")) - self.assertEqual(psutil.cpu_count(logical=True), num) + assert psutil.cpu_count(logical=True) == num - @unittest.skipIf(not which("lscpu"), "lscpu utility not available") + @pytest.mark.skipif( + not which("lscpu"), reason="lscpu utility not available" + ) def test_against_lscpu(self): out = sh("lscpu -p") num = len([x for x in out.split('\n') if not x.startswith('#')]) - self.assertEqual(psutil.cpu_count(logical=True), num) + assert psutil.cpu_count(logical=True) == num def test_emulate_fallbacks(self): import psutil._pslinux @@ -770,16 +742,16 @@ def test_emulate_fallbacks(self): with mock.patch( 'psutil._pslinux.os.sysconf', side_effect=ValueError ) as m: - self.assertEqual(psutil._pslinux.cpu_count_logical(), original) + assert psutil._pslinux.cpu_count_logical() == original assert m.called # Let's have open() return empty data and make sure None is # returned ('cause we mimic os.cpu_count()). with mock.patch('psutil._common.open', create=True) as m: - self.assertIsNone(psutil._pslinux.cpu_count_logical()) - self.assertEqual(m.call_count, 2) + assert psutil._pslinux.cpu_count_logical() is None + assert m.call_count == 2 # /proc/stat should be the last one - self.assertEqual(m.call_args[0][0], '/proc/stat') + assert m.call_args[0][0] == '/proc/stat' # Let's push this a bit further and make sure /proc/cpuinfo # parsing works as expected. @@ -789,18 +761,20 @@ def test_emulate_fallbacks(self): with mock.patch( 'psutil._common.open', return_value=fake_file, create=True ) as m: - self.assertEqual(psutil._pslinux.cpu_count_logical(), original) + assert psutil._pslinux.cpu_count_logical() == original # Finally, let's make /proc/cpuinfo return meaningless data; # this way we'll fall back on relying on /proc/stat with mock_open_content({"/proc/cpuinfo": b""}) as m: - self.assertEqual(psutil._pslinux.cpu_count_logical(), original) + assert psutil._pslinux.cpu_count_logical() == original assert m.called -@unittest.skipIf(not LINUX, "LINUX only") +@pytest.mark.skipif(not LINUX, reason="LINUX only") class TestSystemCPUCountCores(PsutilTestCase): - @unittest.skipIf(not which("lscpu"), "lscpu utility not available") + @pytest.mark.skipif( + not which("lscpu"), reason="lscpu utility not available" + ) def test_against_lscpu(self): out = sh("lscpu -p") core_ids = set() @@ -808,7 +782,7 @@ def test_against_lscpu(self): if not line.startswith('#'): fields = line.split(',') core_ids.add(fields[1]) - self.assertEqual(psutil.cpu_count(logical=False), len(core_ids)) + assert psutil.cpu_count(logical=False) == len(core_ids) def test_method_2(self): meth_1 = psutil._pslinux.cpu_count_cores() @@ -816,19 +790,19 @@ def test_method_2(self): meth_2 = psutil._pslinux.cpu_count_cores() assert m.called if meth_1 is not None: - self.assertEqual(meth_1, meth_2) + assert meth_1 == meth_2 def test_emulate_none(self): with mock.patch('glob.glob', return_value=[]) as m1: with mock.patch('psutil._common.open', create=True) as m2: - self.assertIsNone(psutil._pslinux.cpu_count_cores()) + assert psutil._pslinux.cpu_count_cores() is None assert m1.called assert m2.called -@unittest.skipIf(not LINUX, "LINUX only") +@pytest.mark.skipif(not LINUX, reason="LINUX only") class TestSystemCPUFrequency(PsutilTestCase): - @unittest.skipIf(not HAS_CPU_FREQ, "not supported") + @pytest.mark.skipif(not HAS_CPU_FREQ, reason="not supported") def test_emulate_use_second_file(self): # https://github.com/giampaolo/psutil/issues/981 def path_exists_mock(path): @@ -843,8 +817,10 @@ def path_exists_mock(path): ): assert psutil.cpu_freq() - @unittest.skipIf(not HAS_CPU_FREQ, "not supported") - @unittest.skipIf(AARCH64, "aarch64 does not report mhz in /proc/cpuinfo") + @pytest.mark.skipif(not HAS_CPU_FREQ, reason="not supported") + @pytest.mark.skipif( + AARCH64, reason="aarch64 does not report mhz in /proc/cpuinfo" + ) def test_emulate_use_cpuinfo(self): # Emulate a case where /sys/devices/system/cpu/cpufreq* does not # exist and /proc/cpuinfo is used instead. @@ -860,16 +836,16 @@ def path_exists_mock(path): reload_module(psutil._pslinux) ret = psutil.cpu_freq() assert ret, ret - self.assertEqual(ret.max, 0.0) - self.assertEqual(ret.min, 0.0) + assert ret.max == 0.0 + assert ret.min == 0.0 for freq in psutil.cpu_freq(percpu=True): - self.assertEqual(freq.max, 0.0) - self.assertEqual(freq.min, 0.0) + assert freq.max == 0.0 + assert freq.min == 0.0 finally: reload_module(psutil._pslinux) reload_module(psutil) - @unittest.skipIf(not HAS_CPU_FREQ, "not supported") + @pytest.mark.skipif(not HAS_CPU_FREQ, reason="not supported") def test_emulate_data(self): def open_mock(name, *args, **kwargs): if name.endswith('/scaling_cur_freq') and name.startswith( @@ -894,15 +870,15 @@ def open_mock(name, *args, **kwargs): with mock.patch(patch_point, side_effect=open_mock): with mock.patch('os.path.exists', return_value=True): freq = psutil.cpu_freq() - self.assertEqual(freq.current, 500.0) + assert freq.current == 500.0 # when /proc/cpuinfo is used min and max frequencies are not # available and are set to 0. if freq.min != 0.0: - self.assertEqual(freq.min, 600.0) + assert freq.min == 600.0 if freq.max != 0.0: - self.assertEqual(freq.max, 700.0) + assert freq.max == 700.0 - @unittest.skipIf(not HAS_CPU_FREQ, "not supported") + @pytest.mark.skipif(not HAS_CPU_FREQ, reason="not supported") def test_emulate_multi_cpu(self): def open_mock(name, *args, **kwargs): n = name @@ -943,18 +919,18 @@ def open_mock(name, *args, **kwargs): 'psutil._pslinux.cpu_count_logical', return_value=2 ): freq = psutil.cpu_freq(percpu=True) - self.assertEqual(freq[0].current, 100.0) + assert freq[0].current == 100.0 if freq[0].min != 0.0: - self.assertEqual(freq[0].min, 200.0) + assert freq[0].min == 200.0 if freq[0].max != 0.0: - self.assertEqual(freq[0].max, 300.0) - self.assertEqual(freq[1].current, 400.0) + assert freq[0].max == 300.0 + assert freq[1].current == 400.0 if freq[1].min != 0.0: - self.assertEqual(freq[1].min, 500.0) + assert freq[1].min == 500.0 if freq[1].max != 0.0: - self.assertEqual(freq[1].max, 600.0) + assert freq[1].max == 600.0 - @unittest.skipIf(not HAS_CPU_FREQ, "not supported") + @pytest.mark.skipif(not HAS_CPU_FREQ, reason="not supported") def test_emulate_no_scaling_cur_freq_file(self): # See: https://github.com/giampaolo/psutil/issues/1071 def open_mock(name, *args, **kwargs): @@ -975,33 +951,35 @@ def open_mock(name, *args, **kwargs): 'psutil._pslinux.cpu_count_logical', return_value=1 ): freq = psutil.cpu_freq() - self.assertEqual(freq.current, 200) + assert freq.current == 200 -@unittest.skipIf(not LINUX, "LINUX only") +@pytest.mark.skipif(not LINUX, reason="LINUX only") class TestSystemCPUStats(PsutilTestCase): - def test_ctx_switches(self): - vmstat_value = vmstat("context switches") - psutil_value = psutil.cpu_stats().ctx_switches - self.assertAlmostEqual(vmstat_value, psutil_value, delta=500) + + # XXX: fails too often. + # def test_ctx_switches(self): + # vmstat_value = vmstat("context switches") + # psutil_value = psutil.cpu_stats().ctx_switches + # self.assertAlmostEqual(vmstat_value, psutil_value, delta=500) def test_interrupts(self): vmstat_value = vmstat("interrupts") psutil_value = psutil.cpu_stats().interrupts - self.assertAlmostEqual(vmstat_value, psutil_value, delta=500) + assert abs(vmstat_value - psutil_value) < 500 -@unittest.skipIf(not LINUX, "LINUX only") +@pytest.mark.skipif(not LINUX, reason="LINUX only") class TestLoadAvg(PsutilTestCase): - @unittest.skipIf(not HAS_GETLOADAVG, "not supported") + @pytest.mark.skipif(not HAS_GETLOADAVG, reason="not supported") def test_getloadavg(self): psutil_value = psutil.getloadavg() with open("/proc/loadavg") as f: proc_value = f.read().split() - self.assertAlmostEqual(float(proc_value[0]), psutil_value[0], delta=1) - self.assertAlmostEqual(float(proc_value[1]), psutil_value[1], delta=1) - self.assertAlmostEqual(float(proc_value[2]), psutil_value[2], delta=1) + assert abs(float(proc_value[0]) - psutil_value[0]) < 1 + assert abs(float(proc_value[1]) - psutil_value[1]) < 1 + assert abs(float(proc_value[2]) - psutil_value[2]) < 1 # ===================================================================== @@ -1009,22 +987,20 @@ def test_getloadavg(self): # ===================================================================== -@unittest.skipIf(not LINUX, "LINUX only") +@pytest.mark.skipif(not LINUX, reason="LINUX only") class TestSystemNetIfAddrs(PsutilTestCase): def test_ips(self): for name, addrs in psutil.net_if_addrs().items(): for addr in addrs: if addr.family == psutil.AF_LINK: - self.assertEqual(addr.address, get_mac_address(name)) + assert addr.address == get_mac_address(name) elif addr.family == socket.AF_INET: - self.assertEqual(addr.address, get_ipv4_address(name)) - self.assertEqual(addr.netmask, get_ipv4_netmask(name)) + assert addr.address == get_ipv4_address(name) + assert addr.netmask == get_ipv4_netmask(name) if addr.broadcast is not None: - self.assertEqual( - addr.broadcast, get_ipv4_broadcast(name) - ) + assert addr.broadcast == get_ipv4_broadcast(name) else: - self.assertEqual(get_ipv4_broadcast(name), '0.0.0.0') + assert get_ipv4_broadcast(name) == '0.0.0.0' elif addr.family == socket.AF_INET6: # IPv6 addresses can have a percent symbol at the end. # E.g. these 2 are equivalent: @@ -1033,10 +1009,10 @@ def test_ips(self): # That is the "zone id" portion, which usually is the name # of the network interface. address = addr.address.split('%')[0] - self.assertIn(address, get_ipv6_addresses(name)) + assert address in get_ipv6_addresses(name) # XXX - not reliable when having virtual NICs installed by Docker. - # @unittest.skipIf(not which('ip'), "'ip' utility not available") + # @pytest.mark.skipif(not 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] @@ -1051,10 +1027,12 @@ def test_ips(self): # pprint.pformat(nics), out)) -@unittest.skipIf(not LINUX, "LINUX only") -@unittest.skipIf(QEMU_USER, "QEMU user not supported") +@pytest.mark.skipif(not LINUX, reason="LINUX only") +@pytest.mark.skipif(QEMU_USER, reason="QEMU user not supported") class TestSystemNetIfStats(PsutilTestCase): - @unittest.skipIf(not which("ifconfig"), "ifconfig utility not available") + @pytest.mark.skipif( + not which("ifconfig"), reason="ifconfig utility not available" + ) def test_against_ifconfig(self): for name, stats in psutil.net_if_stats().items(): try: @@ -1062,17 +1040,19 @@ def test_against_ifconfig(self): except RuntimeError: pass else: - self.assertEqual(stats.isup, 'RUNNING' in out, msg=out) - self.assertEqual( - stats.mtu, int(re.findall(r'(?i)MTU[: ](\d+)', out)[0]) + assert stats.isup == ('RUNNING' in out), out + assert stats.mtu == int( + re.findall(r'(?i)MTU[: ](\d+)', out)[0] ) def test_mtu(self): for name, stats in psutil.net_if_stats().items(): with open("/sys/class/net/%s/mtu" % name) as f: - self.assertEqual(stats.mtu, int(f.read().strip())) + assert stats.mtu == int(f.read().strip()) - @unittest.skipIf(not which("ifconfig"), "ifconfig utility not available") + @pytest.mark.skipif( + not which("ifconfig"), reason="ifconfig utility not available" + ) def test_flags(self): # first line looks like this: # "eth0: flags=4163 mtu 1500" @@ -1088,7 +1068,7 @@ def test_flags(self): matches_found += 1 ifconfig_flags = set(match.group(2).lower().split(",")) psutil_flags = set(stats.flags.split(",")) - self.assertEqual(ifconfig_flags, psutil_flags) + assert ifconfig_flags == psutil_flags else: # ifconfig has a different output on CentOS 6 # let's try that @@ -1097,15 +1077,17 @@ def test_flags(self): matches_found += 1 ifconfig_flags = set(match.group(1).lower().split()) psutil_flags = set(stats.flags.split(",")) - self.assertEqual(ifconfig_flags, psutil_flags) + assert ifconfig_flags == psutil_flags if not matches_found: raise self.fail("no matches were found") -@unittest.skipIf(not LINUX, "LINUX only") +@pytest.mark.skipif(not LINUX, reason="LINUX only") class TestSystemNetIOCounters(PsutilTestCase): - @unittest.skipIf(not which("ifconfig"), "ifconfig utility not available") + @pytest.mark.skipif( + not which("ifconfig"), reason="ifconfig utility not available" + ) @retry_on_failure() def test_against_ifconfig(self): def ifconfig(nic): @@ -1135,33 +1117,25 @@ def ifconfig(nic): ifconfig_ret = ifconfig(name) except RuntimeError: continue - self.assertAlmostEqual( - stats.bytes_recv, ifconfig_ret['bytes_recv'], delta=1024 * 10 - ) - self.assertAlmostEqual( - stats.bytes_sent, ifconfig_ret['bytes_sent'], delta=1024 * 10 - ) - self.assertAlmostEqual( - stats.packets_recv, ifconfig_ret['packets_recv'], delta=1024 - ) - self.assertAlmostEqual( - stats.packets_sent, ifconfig_ret['packets_sent'], delta=1024 + assert ( + abs(stats.bytes_recv - ifconfig_ret['bytes_recv']) < 1024 * 10 ) - self.assertAlmostEqual( - stats.errin, ifconfig_ret['errin'], delta=10 + assert ( + abs(stats.bytes_sent - ifconfig_ret['bytes_sent']) < 1024 * 10 ) - self.assertAlmostEqual( - stats.errout, ifconfig_ret['errout'], delta=10 + assert ( + abs(stats.packets_recv - ifconfig_ret['packets_recv']) < 1024 ) - self.assertAlmostEqual( - stats.dropin, ifconfig_ret['dropin'], delta=10 - ) - self.assertAlmostEqual( - stats.dropout, ifconfig_ret['dropout'], delta=10 + assert ( + abs(stats.packets_sent - ifconfig_ret['packets_sent']) < 1024 ) + assert abs(stats.errin - ifconfig_ret['errin']) < 10 + assert abs(stats.errout - ifconfig_ret['errout']) < 10 + assert abs(stats.dropin - ifconfig_ret['dropin']) < 10 + assert abs(stats.dropout - ifconfig_ret['dropout']) < 10 -@unittest.skipIf(not LINUX, "LINUX only") +@pytest.mark.skipif(not LINUX, reason="LINUX only") class TestSystemNetConnections(PsutilTestCase): @mock.patch('psutil._pslinux.socket.inet_ntop', side_effect=ValueError) @mock.patch('psutil._pslinux.supports_ipv6', return_value=False) @@ -1192,9 +1166,11 @@ def test_emulate_unix(self): # ===================================================================== -@unittest.skipIf(not LINUX, "LINUX only") +@pytest.mark.skipif(not LINUX, reason="LINUX only") class TestSystemDiskPartitions(PsutilTestCase): - @unittest.skipIf(not hasattr(os, 'statvfs'), "os.statvfs() not available") + @pytest.mark.skipif( + not hasattr(os, 'statvfs'), reason="os.statvfs() not available" + ) @skip_on_not_implemented() def test_against_df(self): # test psutil.disk_usage() and psutil.disk_partitions() @@ -1213,13 +1189,9 @@ def df(path): for part in psutil.disk_partitions(all=False): usage = psutil.disk_usage(part.mountpoint) _, total, used, free = df(part.mountpoint) - self.assertEqual(usage.total, total) - self.assertAlmostEqual( - usage.free, free, delta=TOLERANCE_DISK_USAGE - ) - self.assertAlmostEqual( - usage.used, used, delta=TOLERANCE_DISK_USAGE - ) + assert usage.total == total + assert abs(usage.free - free) < TOLERANCE_DISK_USAGE + assert abs(usage.used - used) < TOLERANCE_DISK_USAGE def test_zfs_fs(self): # Test that ZFS partitions are returned. @@ -1245,7 +1217,7 @@ def test_zfs_fs(self): assert m1.called assert m2.called assert ret - self.assertEqual(ret[0].fstype, 'zfs') + assert ret[0].fstype == 'zfs' def test_emulate_realpath_fail(self): # See: https://github.com/giampaolo/psutil/issues/1307 @@ -1253,14 +1225,14 @@ def test_emulate_realpath_fail(self): with mock.patch( 'os.path.realpath', return_value='/non/existent' ) as m: - with self.assertRaises(FileNotFoundError): + with pytest.raises(FileNotFoundError): psutil.disk_partitions() assert m.called finally: psutil.PROCFS_PATH = "/proc" -@unittest.skipIf(not LINUX, "LINUX only") +@pytest.mark.skipif(not LINUX, reason="LINUX only") class TestSystemDiskIoCounters(PsutilTestCase): def test_emulate_kernel_2_4(self): # Tests /proc/diskstats parsing format for 2.4 kernels, see: @@ -1271,15 +1243,15 @@ def test_emulate_kernel_2_4(self): 'psutil._pslinux.is_storage_device', return_value=True ): ret = psutil.disk_io_counters(nowrap=False) - self.assertEqual(ret.read_count, 1) - self.assertEqual(ret.read_merged_count, 2) - self.assertEqual(ret.read_bytes, 3 * SECTOR_SIZE) - self.assertEqual(ret.read_time, 4) - self.assertEqual(ret.write_count, 5) - self.assertEqual(ret.write_merged_count, 6) - self.assertEqual(ret.write_bytes, 7 * SECTOR_SIZE) - self.assertEqual(ret.write_time, 8) - self.assertEqual(ret.busy_time, 10) + assert ret.read_count == 1 + assert ret.read_merged_count == 2 + assert ret.read_bytes == 3 * SECTOR_SIZE + assert ret.read_time == 4 + assert ret.write_count == 5 + assert ret.write_merged_count == 6 + assert ret.write_bytes == 7 * SECTOR_SIZE + assert ret.write_time == 8 + assert ret.busy_time == 10 def test_emulate_kernel_2_6_full(self): # Tests /proc/diskstats parsing format for 2.6 kernels, @@ -1291,15 +1263,15 @@ def test_emulate_kernel_2_6_full(self): 'psutil._pslinux.is_storage_device', return_value=True ): ret = psutil.disk_io_counters(nowrap=False) - self.assertEqual(ret.read_count, 1) - self.assertEqual(ret.read_merged_count, 2) - self.assertEqual(ret.read_bytes, 3 * SECTOR_SIZE) - self.assertEqual(ret.read_time, 4) - self.assertEqual(ret.write_count, 5) - self.assertEqual(ret.write_merged_count, 6) - self.assertEqual(ret.write_bytes, 7 * SECTOR_SIZE) - self.assertEqual(ret.write_time, 8) - self.assertEqual(ret.busy_time, 10) + assert ret.read_count == 1 + assert ret.read_merged_count == 2 + assert ret.read_bytes == 3 * SECTOR_SIZE + assert ret.read_time == 4 + assert ret.write_count == 5 + assert ret.write_merged_count == 6 + assert ret.write_bytes == 7 * SECTOR_SIZE + assert ret.write_time == 8 + assert ret.busy_time == 10 def test_emulate_kernel_2_6_limited(self): # Tests /proc/diskstats parsing format for 2.6 kernels, @@ -1312,16 +1284,16 @@ def test_emulate_kernel_2_6_limited(self): 'psutil._pslinux.is_storage_device', return_value=True ): ret = psutil.disk_io_counters(nowrap=False) - self.assertEqual(ret.read_count, 1) - self.assertEqual(ret.read_bytes, 2 * SECTOR_SIZE) - self.assertEqual(ret.write_count, 3) - self.assertEqual(ret.write_bytes, 4 * SECTOR_SIZE) + assert ret.read_count == 1 + assert ret.read_bytes == 2 * SECTOR_SIZE + assert ret.write_count == 3 + assert ret.write_bytes == 4 * SECTOR_SIZE - self.assertEqual(ret.read_merged_count, 0) - self.assertEqual(ret.read_time, 0) - self.assertEqual(ret.write_merged_count, 0) - self.assertEqual(ret.write_time, 0) - self.assertEqual(ret.busy_time, 0) + assert ret.read_merged_count == 0 + assert ret.read_time == 0 + assert ret.write_merged_count == 0 + assert ret.write_time == 0 + assert ret.busy_time == 0 def test_emulate_include_partitions(self): # Make sure that when perdisk=True disk partitions are returned, @@ -1336,11 +1308,11 @@ def test_emulate_include_partitions(self): 'psutil._pslinux.is_storage_device', return_value=False ): ret = psutil.disk_io_counters(perdisk=True, nowrap=False) - self.assertEqual(len(ret), 2) - self.assertEqual(ret['nvme0n1'].read_count, 1) - self.assertEqual(ret['nvme0n1p1'].read_count, 1) - self.assertEqual(ret['nvme0n1'].write_count, 5) - self.assertEqual(ret['nvme0n1p1'].write_count, 5) + assert len(ret) == 2 + assert ret['nvme0n1'].read_count == 1 + assert ret['nvme0n1p1'].read_count == 1 + assert ret['nvme0n1'].write_count == 5 + assert ret['nvme0n1p1'].write_count == 5 def test_emulate_exclude_partitions(self): # Make sure that when perdisk=False partitions (e.g. 'sda1', @@ -1355,7 +1327,7 @@ def test_emulate_exclude_partitions(self): 'psutil._pslinux.is_storage_device', return_value=False ): ret = psutil.disk_io_counters(perdisk=False, nowrap=False) - self.assertIsNone(ret) + assert ret is None def is_storage_device(name): return name == 'nvme0n1' @@ -1371,8 +1343,8 @@ def is_storage_device(name): side_effect=is_storage_device, ): ret = psutil.disk_io_counters(perdisk=False, nowrap=False) - self.assertEqual(ret.read_count, 1) - self.assertEqual(ret.write_count, 5) + assert ret.read_count == 1 + assert ret.write_count == 5 def test_emulate_use_sysfs(self): def exists(path): @@ -1383,7 +1355,7 @@ def exists(path): 'psutil._pslinux.os.path.exists', create=True, side_effect=exists ): wsysfs = psutil.disk_io_counters(perdisk=True) - self.assertEqual(len(wprocfs), len(wsysfs)) + assert len(wprocfs) == len(wsysfs) def test_emulate_not_impl(self): def exists(path): @@ -1392,10 +1364,11 @@ def exists(path): with mock.patch( 'psutil._pslinux.os.path.exists', create=True, side_effect=exists ): - self.assertRaises(NotImplementedError, psutil.disk_io_counters) + with pytest.raises(NotImplementedError): + psutil.disk_io_counters() -@unittest.skipIf(not LINUX, "LINUX only") +@pytest.mark.skipif(not LINUX, reason="LINUX only") class TestRootFsDeviceFinder(PsutilTestCase): def setUp(self): dev = os.stat("/").st_dev @@ -1407,19 +1380,21 @@ def test_call_methods(self): if os.path.exists("/proc/partitions"): finder.ask_proc_partitions() else: - self.assertRaises(FileNotFoundError, finder.ask_proc_partitions) + with pytest.raises(FileNotFoundError): + finder.ask_proc_partitions() if os.path.exists( "/sys/dev/block/%s:%s/uevent" % (self.major, self.minor) ): finder.ask_sys_dev_block() else: - self.assertRaises(FileNotFoundError, finder.ask_sys_dev_block) + with pytest.raises(FileNotFoundError): + finder.ask_sys_dev_block() finder.ask_sys_class_block() - @unittest.skipIf(GITHUB_ACTIONS, "unsupported on GITHUB_ACTIONS") + @pytest.mark.skipif(GITHUB_ACTIONS, reason="unsupported on GITHUB_ACTIONS") def test_comparisons(self): finder = RootFsDeviceFinder() - self.assertIsNotNone(finder.find()) + assert finder.find() is not None a = b = c = None if os.path.exists("/proc/partitions"): @@ -1432,18 +1407,20 @@ def test_comparisons(self): base = a or b or c if base and a: - self.assertEqual(base, a) + assert base == a if base and b: - self.assertEqual(base, b) + assert base == b if base and c: - self.assertEqual(base, c) + assert base == c - @unittest.skipIf(not which("findmnt"), "findmnt utility not available") - @unittest.skipIf(GITHUB_ACTIONS, "unsupported on GITHUB_ACTIONS") + @pytest.mark.skipif( + not which("findmnt"), reason="findmnt utility not available" + ) + @pytest.mark.skipif(GITHUB_ACTIONS, reason="unsupported on GITHUB_ACTIONS") def test_against_findmnt(self): psutil_value = RootFsDeviceFinder().find() findmnt_value = sh("findmnt -o SOURCE -rn /") - self.assertEqual(psutil_value, findmnt_value) + assert psutil_value == findmnt_value def test_disk_partitions_mocked(self): with mock.patch( @@ -1453,10 +1430,10 @@ def test_disk_partitions_mocked(self): part = psutil.disk_partitions()[0] assert m.called if not GITHUB_ACTIONS: - self.assertNotEqual(part.device, "/dev/root") - self.assertEqual(part.device, RootFsDeviceFinder().find()) + assert part.device != "/dev/root" + assert part.device == RootFsDeviceFinder().find() else: - self.assertEqual(part.device, "/dev/root") + assert part.device == "/dev/root" # ===================================================================== @@ -1464,12 +1441,12 @@ def test_disk_partitions_mocked(self): # ===================================================================== -@unittest.skipIf(not LINUX, "LINUX only") +@pytest.mark.skipif(not LINUX, reason="LINUX only") class TestMisc(PsutilTestCase): def test_boot_time(self): vmstat_value = vmstat('boot time') psutil_value = psutil.boot_time() - self.assertEqual(int(vmstat_value), int(psutil_value)) + assert int(vmstat_value) == int(psutil_value) def test_no_procfs_on_import(self): my_procfs = self.get_testfn() @@ -1492,28 +1469,32 @@ def open_mock(name, *args, **kwargs): with mock.patch(patch_point, side_effect=open_mock): reload_module(psutil) - self.assertRaises(IOError, psutil.cpu_times) - self.assertRaises(IOError, psutil.cpu_times, percpu=True) - self.assertRaises(IOError, psutil.cpu_percent) - self.assertRaises(IOError, psutil.cpu_percent, percpu=True) - self.assertRaises(IOError, psutil.cpu_times_percent) - self.assertRaises( - IOError, psutil.cpu_times_percent, percpu=True - ) + with pytest.raises(IOError): + psutil.cpu_times() + with pytest.raises(IOError): + psutil.cpu_times(percpu=True) + with pytest.raises(IOError): + psutil.cpu_percent() + with pytest.raises(IOError): + psutil.cpu_percent(percpu=True) + with pytest.raises(IOError): + psutil.cpu_times_percent() + with pytest.raises(IOError): + psutil.cpu_times_percent(percpu=True) psutil.PROCFS_PATH = my_procfs - self.assertEqual(psutil.cpu_percent(), 0) - self.assertEqual(sum(psutil.cpu_times_percent()), 0) + assert psutil.cpu_percent() == 0 + assert sum(psutil.cpu_times_percent()) == 0 # since we don't know the number of CPUs at import time, # we awkwardly say there are none until the second call per_cpu_percent = psutil.cpu_percent(percpu=True) - self.assertEqual(sum(per_cpu_percent), 0) + assert sum(per_cpu_percent) == 0 # ditto awkward length per_cpu_times_percent = psutil.cpu_times_percent(percpu=True) - self.assertEqual(sum(map(sum, per_cpu_times_percent)), 0) + assert sum(map(sum, per_cpu_times_percent)) == 0 # much user, very busy with open(os.path.join(my_procfs, 'stat'), 'w') as f: @@ -1521,17 +1502,17 @@ def open_mock(name, *args, **kwargs): f.write('cpu0 1 0 0 0 0 0 0 0 0 0\n') f.write('cpu1 1 0 0 0 0 0 0 0 0 0\n') - self.assertNotEqual(psutil.cpu_percent(), 0) - self.assertNotEqual(sum(psutil.cpu_percent(percpu=True)), 0) - self.assertNotEqual(sum(psutil.cpu_times_percent()), 0) - self.assertNotEqual( - sum(map(sum, psutil.cpu_times_percent(percpu=True))), 0 + assert psutil.cpu_percent() != 0 + assert sum(psutil.cpu_percent(percpu=True)) != 0 + assert sum(psutil.cpu_times_percent()) != 0 + assert ( + sum(map(sum, psutil.cpu_times_percent(percpu=True))) != 0 ) finally: shutil.rmtree(my_procfs) reload_module(psutil) - self.assertEqual(psutil.PROCFS_PATH, '/proc') + assert psutil.PROCFS_PATH == '/proc' def test_cpu_steal_decrease(self): # Test cumulative cpu stats decrease. We should ignore this. @@ -1563,46 +1544,57 @@ def test_cpu_steal_decrease(self): cpu_percent_percpu = psutil.cpu_percent(percpu=True) cpu_times_percent = psutil.cpu_times_percent() cpu_times_percent_percpu = psutil.cpu_times_percent(percpu=True) - self.assertNotEqual(cpu_percent, 0) - self.assertNotEqual(sum(cpu_percent_percpu), 0) - self.assertNotEqual(sum(cpu_times_percent), 0) - self.assertNotEqual(sum(cpu_times_percent), 100.0) - self.assertNotEqual(sum(map(sum, cpu_times_percent_percpu)), 0) - self.assertNotEqual(sum(map(sum, cpu_times_percent_percpu)), 100.0) - self.assertEqual(cpu_times_percent.steal, 0) - self.assertNotEqual(cpu_times_percent.user, 0) + assert cpu_percent != 0 + assert sum(cpu_percent_percpu) != 0 + assert sum(cpu_times_percent) != 0 + assert sum(cpu_times_percent) != 100.0 + assert sum(map(sum, cpu_times_percent_percpu)) != 0 + assert sum(map(sum, cpu_times_percent_percpu)) != 100.0 + assert cpu_times_percent.steal == 0 + assert cpu_times_percent.user != 0 def test_boot_time_mocked(self): with mock.patch('psutil._common.open', create=True) as m: - self.assertRaises(RuntimeError, psutil._pslinux.boot_time) + with pytest.raises(RuntimeError): + psutil._pslinux.boot_time() assert m.called def test_users(self): # Make sure the C extension converts ':0' and ':0.0' to # 'localhost'. for user in psutil.users(): - self.assertNotIn(user.host, (":0", ":0.0")) + assert user.host not in (":0", ":0.0") def test_procfs_path(self): tdir = self.get_testfn() os.mkdir(tdir) try: psutil.PROCFS_PATH = tdir - self.assertRaises(IOError, psutil.virtual_memory) - self.assertRaises(IOError, psutil.cpu_times) - self.assertRaises(IOError, psutil.cpu_times, percpu=True) - self.assertRaises(IOError, psutil.boot_time) + with pytest.raises(IOError): + psutil.virtual_memory() + with pytest.raises(IOError): + psutil.cpu_times() + with pytest.raises(IOError): + psutil.cpu_times(percpu=True) + with pytest.raises(IOError): + psutil.boot_time() # self.assertRaises(IOError, psutil.pids) - self.assertRaises(IOError, psutil.net_connections) - self.assertRaises(IOError, psutil.net_io_counters) - self.assertRaises(IOError, psutil.net_if_stats) + with pytest.raises(IOError): + psutil.net_connections() + with pytest.raises(IOError): + psutil.net_io_counters() + with pytest.raises(IOError): + psutil.net_if_stats() # self.assertRaises(IOError, psutil.disk_io_counters) - self.assertRaises(IOError, psutil.disk_partitions) - self.assertRaises(psutil.NoSuchProcess, psutil.Process) + with pytest.raises(IOError): + psutil.disk_partitions() + with pytest.raises(psutil.NoSuchProcess): + psutil.Process() finally: psutil.PROCFS_PATH = "/proc" @retry_on_failure() + @pytest.mark.skipif(PYTEST_PARALLEL, reason="skip if pytest-parallel") def test_issue_687(self): # In case of thread ID: # - pid_exists() is supposed to return False @@ -1612,12 +1604,12 @@ def test_issue_687(self): with ThreadTask(): p = psutil.Process() threads = p.threads() - self.assertEqual(len(threads), 3 if QEMU_USER else 2) + assert len(threads) == (3 if QEMU_USER else 2) tid = sorted(threads, key=lambda x: x.id)[1].id - self.assertNotEqual(p.pid, tid) + assert p.pid != tid pt = psutil.Process(tid) pt.as_dict() - self.assertNotIn(tid, psutil.pids()) + assert tid not in psutil.pids() def test_pid_exists_no_proc_status(self): # Internally pid_exists relies on /proc/{pid}/status. @@ -1633,15 +1625,15 @@ def test_pid_exists_no_proc_status(self): # ===================================================================== -@unittest.skipIf(not LINUX, "LINUX only") -@unittest.skipIf(not HAS_BATTERY, "no battery") +@pytest.mark.skipif(not LINUX, reason="LINUX only") +@pytest.mark.skipif(not HAS_BATTERY, reason="no battery") class TestSensorsBattery(PsutilTestCase): - @unittest.skipIf(not which("acpi"), "acpi utility not available") + @pytest.mark.skipif(not which("acpi"), reason="acpi utility not available") def test_percent(self): out = sh("acpi -b") acpi_value = int(out.split(",")[1].strip().replace('%', '')) psutil_value = psutil.sensors_battery().percent - self.assertAlmostEqual(acpi_value, psutil_value, delta=1) + assert abs(acpi_value - psutil_value) < 1 def test_emulate_power_plugged(self): # Pretend the AC power cable is connected. @@ -1654,9 +1646,10 @@ def open_mock(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: - self.assertEqual(psutil.sensors_battery().power_plugged, True) - self.assertEqual( - psutil.sensors_battery().secsleft, psutil.POWER_TIME_UNLIMITED + assert psutil.sensors_battery().power_plugged is True + assert ( + psutil.sensors_battery().secsleft + == psutil.POWER_TIME_UNLIMITED ) assert m.called @@ -1674,7 +1667,7 @@ def open_mock(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: - self.assertEqual(psutil.sensors_battery().power_plugged, True) + assert psutil.sensors_battery().power_plugged is True assert m.called def test_emulate_power_not_plugged(self): @@ -1688,7 +1681,7 @@ def open_mock(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: - self.assertEqual(psutil.sensors_battery().power_plugged, False) + assert psutil.sensors_battery().power_plugged is False assert m.called def test_emulate_power_not_plugged_2(self): @@ -1705,7 +1698,7 @@ def open_mock(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: - self.assertEqual(psutil.sensors_battery().power_plugged, False) + assert psutil.sensors_battery().power_plugged is False assert m.called def test_emulate_power_undetermined(self): @@ -1725,7 +1718,7 @@ def open_mock(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: - self.assertIsNone(psutil.sensors_battery().power_plugged) + assert psutil.sensors_battery().power_plugged is None assert m.called def test_emulate_energy_full_0(self): @@ -1733,7 +1726,7 @@ def test_emulate_energy_full_0(self): with mock_open_content( {"/sys/class/power_supply/BAT0/energy_full": b"0"} ) as m: - self.assertEqual(psutil.sensors_battery().percent, 0) + assert psutil.sensors_battery().percent == 0 assert m.called def test_emulate_energy_full_not_avail(self): @@ -1750,7 +1743,7 @@ def test_emulate_energy_full_not_avail(self): with mock_open_content( {"/sys/class/power_supply/BAT0/capacity": b"88"} ): - self.assertEqual(psutil.sensors_battery().percent, 88) + assert psutil.sensors_battery().percent == 88 def test_emulate_no_power(self): # Emulate a case where /AC0/online file nor /BAT0/status exist. @@ -1764,10 +1757,10 @@ def test_emulate_no_power(self): "/sys/class/power_supply/BAT0/status", IOError(errno.ENOENT, ""), ): - self.assertIsNone(psutil.sensors_battery().power_plugged) + assert psutil.sensors_battery().power_plugged is None -@unittest.skipIf(not LINUX, "LINUX only") +@pytest.mark.skipif(not LINUX, reason="LINUX only") class TestSensorsBatteryEmulated(PsutilTestCase): def test_it(self): def open_mock(name, *args, **kwargs): @@ -1784,12 +1777,12 @@ def open_mock(name, *args, **kwargs): 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: - self.assertIsNotNone(psutil.sensors_battery()) + assert psutil.sensors_battery() is not None assert mlistdir.called assert mopen.called -@unittest.skipIf(not LINUX, "LINUX only") +@pytest.mark.skipif(not LINUX, reason="LINUX only") class TestSensorsTemperatures(PsutilTestCase): def test_emulate_class_hwmon(self): def open_mock(name, *args, **kwargs): @@ -1814,10 +1807,10 @@ def open_mock(name, *args, **kwargs): 'glob.glob', return_value=['/sys/class/hwmon/hwmon0/temp1'] ): temp = psutil.sensors_temperatures()['name'][0] - self.assertEqual(temp.label, 'label') - self.assertEqual(temp.current, 30.0) - self.assertEqual(temp.high, 40.0) - self.assertEqual(temp.critical, 50.0) + assert temp.label == 'label' + assert temp.current == 30.0 + assert temp.high == 40.0 + assert temp.critical == 50.0 def test_emulate_class_thermal(self): def open_mock(name, *args, **kwargs): @@ -1851,13 +1844,13 @@ def glob_mock(path): with mock.patch(patch_point, side_effect=open_mock): with mock.patch('glob.glob', create=True, side_effect=glob_mock): temp = psutil.sensors_temperatures()['name'][0] - self.assertEqual(temp.label, '') - self.assertEqual(temp.current, 30.0) - self.assertEqual(temp.high, 50.0) - self.assertEqual(temp.critical, 50.0) + assert temp.label == '' # noqa + assert temp.current == 30.0 + assert temp.high == 50.0 + assert temp.critical == 50.0 -@unittest.skipIf(not LINUX, "LINUX only") +@pytest.mark.skipif(not LINUX, reason="LINUX only") class TestSensorsFans(PsutilTestCase): def test_emulate_data(self): def open_mock(name, *args, **kwargs): @@ -1877,8 +1870,8 @@ def open_mock(name, *args, **kwargs): 'glob.glob', return_value=['/sys/class/hwmon/hwmon2/fan1'] ): fan = psutil.sensors_fans()['name'][0] - self.assertEqual(fan.label, 'label') - self.assertEqual(fan.current, 2000) + assert fan.label == 'label' + assert fan.current == 2000 # ===================================================================== @@ -1886,20 +1879,19 @@ def open_mock(name, *args, **kwargs): # ===================================================================== -@unittest.skipIf(not LINUX, "LINUX only") +@pytest.mark.skipif(not LINUX, reason="LINUX only") class TestProcess(PsutilTestCase): @retry_on_failure() def test_parse_smaps_vs_memory_maps(self): sproc = self.spawn_testproc() uss, pss, swap = psutil._pslinux.Process(sproc.pid)._parse_smaps() maps = psutil.Process(sproc.pid).memory_maps(grouped=False) - self.assertAlmostEqual( - uss, - sum([x.private_dirty + x.private_clean for x in maps]), - delta=4096, + assert ( + abs(uss - sum([x.private_dirty + x.private_clean for x in maps])) + < 4096 ) - self.assertAlmostEqual(pss, sum([x.pss for x in maps]), delta=4096) - self.assertAlmostEqual(swap, sum([x.swap for x in maps]), delta=4096) + assert abs(pss - sum([x.pss for x in maps])) < 4096 + assert abs(swap - sum([x.swap for x in maps])) < 4096 def test_parse_smaps_mocked(self): # See: https://github.com/giampaolo/psutil/issues/1222 @@ -1930,12 +1922,12 @@ def test_parse_smaps_mocked(self): p = psutil._pslinux.Process(os.getpid()) uss, pss, swap = p._parse_smaps() assert m.called - self.assertEqual(uss, (6 + 7 + 14) * 1024) - self.assertEqual(pss, 3 * 1024) - self.assertEqual(swap, 15 * 1024) + assert uss == (6 + 7 + 14) * 1024 + assert pss == 3 * 1024 + assert swap == 15 * 1024 # On PYPY file descriptors are not closed fast enough. - @unittest.skipIf(PYPY, "unreliable on PYPY") + @pytest.mark.skipif(PYPY, reason="unreliable on PYPY") def test_open_files_mode(self): def get_test_file(fname): p = psutil.Process() @@ -1950,25 +1942,25 @@ def get_test_file(fname): testfn = self.get_testfn() with open(testfn, "w"): - self.assertEqual(get_test_file(testfn).mode, "w") + assert get_test_file(testfn).mode == "w" with open(testfn): - self.assertEqual(get_test_file(testfn).mode, "r") + assert get_test_file(testfn).mode == "r" with open(testfn, "a"): - self.assertEqual(get_test_file(testfn).mode, "a") + assert get_test_file(testfn).mode == "a" with open(testfn, "r+"): - self.assertEqual(get_test_file(testfn).mode, "r+") + assert get_test_file(testfn).mode == "r+" with open(testfn, "w+"): - self.assertEqual(get_test_file(testfn).mode, "r+") + assert get_test_file(testfn).mode == "r+" with open(testfn, "a+"): - self.assertEqual(get_test_file(testfn).mode, "a+") + assert get_test_file(testfn).mode == "a+" # note: "x" bit is not supported if PY3: safe_rmpath(testfn) with open(testfn, "x"): - self.assertEqual(get_test_file(testfn).mode, "w") + assert get_test_file(testfn).mode == "w" safe_rmpath(testfn) with open(testfn, "x+"): - self.assertEqual(get_test_file(testfn).mode, "r+") + assert get_test_file(testfn).mode == "r+" def test_open_files_file_gone(self): # simulates a file which gets deleted during open_files() @@ -1977,12 +1969,12 @@ def test_open_files_file_gone(self): files = p.open_files() with open(self.get_testfn(), 'w'): # give the kernel some time to see the new file - call_until(p.open_files, "len(ret) != %i" % len(files)) + call_until(lambda: len(p.open_files()) != len(files)) with mock.patch( 'psutil._pslinux.os.readlink', side_effect=OSError(errno.ENOENT, ""), ) as m: - self.assertEqual(p.open_files(), []) + assert p.open_files() == [] assert m.called # also simulate the case where os.readlink() returns EINVAL # in which case psutil is supposed to 'continue' @@ -1990,7 +1982,7 @@ def test_open_files_file_gone(self): 'psutil._pslinux.os.readlink', side_effect=OSError(errno.EINVAL, ""), ) as m: - self.assertEqual(p.open_files(), []) + assert p.open_files() == [] assert m.called def test_open_files_fd_gone(self): @@ -2001,12 +1993,12 @@ def test_open_files_fd_gone(self): files = p.open_files() with open(self.get_testfn(), 'w'): # give the kernel some time to see the new file - call_until(p.open_files, "len(ret) != %i" % len(files)) + 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, "") ) as m: - self.assertEqual(p.open_files(), []) + assert p.open_files() == [] assert m.called def test_open_files_enametoolong(self): @@ -2017,13 +2009,13 @@ def test_open_files_enametoolong(self): files = p.open_files() with open(self.get_testfn(), 'w'): # give the kernel some time to see the new file - call_until(p.open_files, "len(ret) != %i" % len(files)) + call_until(lambda: len(p.open_files()) != len(files)) patch_point = 'psutil._pslinux.os.readlink' with mock.patch( patch_point, side_effect=OSError(errno.ENAMETOOLONG, "") ) as m: with mock.patch("psutil._pslinux.debug"): - self.assertEqual(p.open_files(), []) + assert p.open_files() == [] assert m.called # --- mocked tests @@ -2032,7 +2024,7 @@ def test_terminal_mocked(self): with mock.patch( 'psutil._pslinux._psposix.get_terminal_map', return_value={} ) as m: - self.assertIsNone(psutil._pslinux.Process(os.getpid()).terminal()) + assert psutil._pslinux.Process(os.getpid()).terminal() is None assert m.called # TODO: re-enable this test. @@ -2050,13 +2042,13 @@ def test_cmdline_mocked(self): with mock.patch( 'psutil._common.open', return_value=fake_file, create=True ) as m: - self.assertEqual(p.cmdline(), ['foo', 'bar']) + assert p.cmdline() == ['foo', 'bar'] assert m.called fake_file = io.StringIO(u'foo\x00bar\x00\x00') with mock.patch( 'psutil._common.open', return_value=fake_file, create=True ) as m: - self.assertEqual(p.cmdline(), ['foo', 'bar', '']) + assert p.cmdline() == ['foo', 'bar', ''] assert m.called def test_cmdline_spaces_mocked(self): @@ -2066,13 +2058,13 @@ def test_cmdline_spaces_mocked(self): with mock.patch( 'psutil._common.open', return_value=fake_file, create=True ) as m: - self.assertEqual(p.cmdline(), ['foo', 'bar']) + assert p.cmdline() == ['foo', 'bar'] assert m.called fake_file = io.StringIO(u'foo bar ') with mock.patch( 'psutil._common.open', return_value=fake_file, create=True ) as m: - self.assertEqual(p.cmdline(), ['foo', 'bar', '']) + assert p.cmdline() == ['foo', 'bar', ''] assert m.called def test_cmdline_mixed_separators(self): @@ -2083,15 +2075,15 @@ def test_cmdline_mixed_separators(self): with mock.patch( 'psutil._common.open', return_value=fake_file, create=True ) as m: - self.assertEqual(p.cmdline(), ['foo', 'bar']) + assert p.cmdline() == ['foo', 'bar'] assert m.called def test_readlink_path_deleted_mocked(self): with mock.patch( 'psutil._pslinux.os.readlink', return_value='/home/foo (deleted)' ): - self.assertEqual(psutil.Process().exe(), "/home/foo") - self.assertEqual(psutil.Process().cwd(), "/home/foo") + assert psutil.Process().exe() == "/home/foo" + assert psutil.Process().cwd() == "/home/foo" def test_threads_mocked(self): # Test the case where os.listdir() returns a file (thread) @@ -2109,7 +2101,7 @@ def open_mock_1(name, *args, **kwargs): with mock.patch(patch_point, side_effect=open_mock_1) as m: ret = psutil.Process().threads() assert m.called - self.assertEqual(ret, []) + assert ret == [] # ...but if it bumps into something != ENOENT we want an # exception. @@ -2120,15 +2112,20 @@ def open_mock_2(name, *args, **kwargs): return orig_open(name, *args, **kwargs) with mock.patch(patch_point, side_effect=open_mock_2): - self.assertRaises(psutil.AccessDenied, psutil.Process().threads) + with pytest.raises(psutil.AccessDenied): + psutil.Process().threads() def test_exe_mocked(self): with mock.patch( 'psutil._pslinux.readlink', side_effect=OSError(errno.ENOENT, "") ) as m: - ret = psutil.Process().exe() - assert m.called - self.assertEqual(ret, "") + # de-activate guessing from cmdline() + with mock.patch( + 'psutil._pslinux.Process.cmdline', return_value=[] + ): + ret = psutil.Process().exe() + assert m.called + assert ret == "" # noqa def test_issue_1014(self): # Emulates a case where smaps file does not exist. In this case @@ -2137,11 +2134,11 @@ def test_issue_1014(self): '/proc/%s/smaps' % os.getpid(), IOError(errno.ENOENT, "") ) as m: p = psutil.Process() - with self.assertRaises(FileNotFoundError): + with pytest.raises(FileNotFoundError): p.memory_maps() assert m.called - @unittest.skipIf(not HAS_RLIMIT, "not supported") + @pytest.mark.skipif(not HAS_RLIMIT, reason="not supported") def test_rlimit_zombie(self): # Emulate a case where rlimit() raises ENOSYS, which may # happen in case of zombie process: @@ -2154,12 +2151,12 @@ def test_rlimit_zombie(self): ) as m2: p = psutil.Process() p.name() - with self.assertRaises(psutil.ZombieProcess) as exc: + with pytest.raises(psutil.ZombieProcess) as cm: p.rlimit(psutil.RLIMIT_NOFILE) assert m1.called assert m2.called - self.assertEqual(exc.exception.pid, p.pid) - self.assertEqual(exc.exception.name, p.name()) + assert cm.value.pid == p.pid + assert cm.value.name == p.name() def test_stat_file_parsing(self): args = [ @@ -2209,19 +2206,17 @@ def test_stat_file_parsing(self): content = " ".join(args).encode() with mock_open_content({"/proc/%s/stat" % os.getpid(): content}): p = psutil.Process() - self.assertEqual(p.name(), 'cat') - self.assertEqual(p.status(), psutil.STATUS_ZOMBIE) - self.assertEqual(p.ppid(), 1) - self.assertEqual( - p.create_time(), 6 / CLOCK_TICKS + psutil.boot_time() - ) + assert p.name() == 'cat' + assert p.status() == psutil.STATUS_ZOMBIE + assert p.ppid() == 1 + assert p.create_time() == 6 / CLOCK_TICKS + psutil.boot_time() cpu = p.cpu_times() - self.assertEqual(cpu.user, 2 / CLOCK_TICKS) - self.assertEqual(cpu.system, 3 / CLOCK_TICKS) - self.assertEqual(cpu.children_user, 4 / CLOCK_TICKS) - self.assertEqual(cpu.children_system, 5 / CLOCK_TICKS) - self.assertEqual(cpu.iowait, 7 / CLOCK_TICKS) - self.assertEqual(p.cpu_num(), 6) + assert cpu.user == 2 / CLOCK_TICKS + assert cpu.system == 3 / CLOCK_TICKS + assert cpu.children_user == 4 / CLOCK_TICKS + assert cpu.children_system == 5 / CLOCK_TICKS + assert cpu.iowait == 7 / CLOCK_TICKS + assert p.cpu_num() == 6 def test_status_file_parsing(self): content = textwrap.dedent("""\ @@ -2234,18 +2229,18 @@ def test_status_file_parsing(self): nonvoluntary_ctxt_switches:\t13""").encode() with mock_open_content({"/proc/%s/status" % os.getpid(): content}): p = psutil.Process() - self.assertEqual(p.num_ctx_switches().voluntary, 12) - self.assertEqual(p.num_ctx_switches().involuntary, 13) - self.assertEqual(p.num_threads(), 66) + assert p.num_ctx_switches().voluntary == 12 + assert p.num_ctx_switches().involuntary == 13 + assert p.num_threads() == 66 uids = p.uids() - self.assertEqual(uids.real, 1000) - self.assertEqual(uids.effective, 1001) - self.assertEqual(uids.saved, 1002) + assert uids.real == 1000 + assert uids.effective == 1001 + assert uids.saved == 1002 gids = p.gids() - self.assertEqual(gids.real, 1004) - self.assertEqual(gids.effective, 1005) - self.assertEqual(gids.saved, 1006) - self.assertEqual(p._proc._get_eligible_cpus(), list(range(8))) + assert gids.real == 1004 + assert gids.effective == 1005 + assert gids.saved == 1006 + assert p._proc._get_eligible_cpus() == list(range(8)) def test_net_connections_enametoolong(self): # Simulate a case where /proc/{pid}/fd/{fd} symlink points to @@ -2257,11 +2252,11 @@ def test_net_connections_enametoolong(self): ) as m: p = psutil.Process() with mock.patch("psutil._pslinux.debug"): - self.assertEqual(p.net_connections(), []) + assert p.net_connections() == [] assert m.called -@unittest.skipIf(not LINUX, "LINUX only") +@pytest.mark.skipif(not LINUX, reason="LINUX only") class TestProcessAgainstStatus(PsutilTestCase): """/proc/pid/stat and /proc/pid/status have many values in common. Whenever possible, psutil uses /proc/pid/stat (it's faster). @@ -2290,47 +2285,45 @@ def read_status_file(self, linestart): def test_name(self): value = self.read_status_file("Name:") - self.assertEqual(self.proc.name(), value) + assert self.proc.name() == value - @unittest.skipIf(QEMU_USER, "QEMU user not supported") + @pytest.mark.skipif(QEMU_USER, reason="QEMU user not supported") def test_status(self): value = self.read_status_file("State:") value = value[value.find('(') + 1 : value.rfind(')')] value = value.replace(' ', '-') - self.assertEqual(self.proc.status(), value) + assert self.proc.status() == value def test_ppid(self): value = self.read_status_file("PPid:") - self.assertEqual(self.proc.ppid(), value) + assert self.proc.ppid() == value def test_num_threads(self): value = self.read_status_file("Threads:") - self.assertEqual(self.proc.num_threads(), value) + assert self.proc.num_threads() == value def test_uids(self): value = self.read_status_file("Uid:") value = tuple(map(int, value.split()[1:4])) - self.assertEqual(self.proc.uids(), value) + assert self.proc.uids() == value def test_gids(self): value = self.read_status_file("Gid:") value = tuple(map(int, value.split()[1:4])) - self.assertEqual(self.proc.gids(), value) + assert self.proc.gids() == value @retry_on_failure() def test_num_ctx_switches(self): value = self.read_status_file("voluntary_ctxt_switches:") - self.assertEqual(self.proc.num_ctx_switches().voluntary, value) + assert self.proc.num_ctx_switches().voluntary == value value = self.read_status_file("nonvoluntary_ctxt_switches:") - self.assertEqual(self.proc.num_ctx_switches().involuntary, value) + assert self.proc.num_ctx_switches().involuntary == value def test_cpu_affinity(self): value = self.read_status_file("Cpus_allowed_list:") if '-' in str(value): min_, max_ = map(int, value.split('-')) - self.assertEqual( - self.proc.cpu_affinity(), list(range(min_, max_ + 1)) - ) + assert self.proc.cpu_affinity() == list(range(min_, max_ + 1)) def test_cpu_affinity_eligible_cpus(self): value = self.read_status_file("Cpus_allowed_list:") @@ -2347,15 +2340,9 @@ def test_cpu_affinity_eligible_cpus(self): # ===================================================================== -@unittest.skipIf(not LINUX, "LINUX only") +@pytest.mark.skipif(not LINUX, reason="LINUX only") class TestUtils(PsutilTestCase): def test_readlink(self): with mock.patch("os.readlink", return_value="foo (deleted)") as m: - self.assertEqual(psutil._psplatform.readlink("bar"), "foo") + assert psutil._psplatform.readlink("bar") == "foo" assert m.called - - -if __name__ == '__main__': - from psutil.tests.runner import run_from_name - - run_from_name(__file__) diff --git a/psutil/tests/test_memleaks.py b/psutil/tests/test_memleaks.py index 6506497c9..e249ca516 100755 --- a/psutil/tests/test_memleaks.py +++ b/psutil/tests/test_memleaks.py @@ -19,8 +19,6 @@ import functools import os import platform -import sys -import unittest import psutil import psutil._common @@ -49,6 +47,7 @@ from psutil.tests import create_sockets from psutil.tests import get_testfn from psutil.tests import process_namespace +from psutil.tests import pytest from psutil.tests import skip_on_access_denied from psutil.tests import spawn_testproc from psutil.tests import system_namespace @@ -113,12 +112,12 @@ def test_exe(self): def test_ppid(self): self.execute(self.proc.ppid) - @unittest.skipIf(not POSIX, "POSIX only") + @pytest.mark.skipif(not POSIX, reason="POSIX only") @fewtimes_if_linux() def test_uids(self): self.execute(self.proc.uids) - @unittest.skipIf(not POSIX, "POSIX only") + @pytest.mark.skipif(not POSIX, reason="POSIX only") @fewtimes_if_linux() def test_gids(self): self.execute(self.proc.gids) @@ -134,11 +133,11 @@ def test_nice_set(self): niceness = thisproc.nice() self.execute(lambda: self.proc.nice(niceness)) - @unittest.skipIf(not HAS_IONICE, "not supported") + @pytest.mark.skipif(not HAS_IONICE, reason="not supported") def test_ionice(self): self.execute(self.proc.ionice) - @unittest.skipIf(not HAS_IONICE, "not supported") + @pytest.mark.skipif(not HAS_IONICE, reason="not supported") def test_ionice_set(self): if WINDOWS: value = thisproc.ionice() @@ -148,12 +147,12 @@ def test_ionice_set(self): fun = functools.partial(cext.proc_ioprio_set, os.getpid(), -1, 0) self.execute_w_exc(OSError, fun) - @unittest.skipIf(not HAS_PROC_IO_COUNTERS, "not supported") + @pytest.mark.skipif(not HAS_PROC_IO_COUNTERS, reason="not supported") @fewtimes_if_linux() def test_io_counters(self): self.execute(self.proc.io_counters) - @unittest.skipIf(POSIX, "worthless on POSIX") + @pytest.mark.skipif(POSIX, reason="worthless on POSIX") def test_username(self): # always open 1 handle on Windows (only once) psutil.Process().username() @@ -168,11 +167,11 @@ def test_create_time(self): def test_num_threads(self): self.execute(self.proc.num_threads) - @unittest.skipIf(not WINDOWS, "WINDOWS only") + @pytest.mark.skipif(not WINDOWS, reason="WINDOWS only") def test_num_handles(self): self.execute(self.proc.num_handles) - @unittest.skipIf(not POSIX, "POSIX only") + @pytest.mark.skipif(not POSIX, reason="POSIX only") @fewtimes_if_linux() def test_num_fds(self): self.execute(self.proc.num_fds) @@ -191,7 +190,7 @@ def test_cpu_times(self): self.execute(self.proc.cpu_times) @fewtimes_if_linux() - @unittest.skipIf(not HAS_PROC_CPU_NUM, "not supported") + @pytest.mark.skipif(not HAS_PROC_CPU_NUM, reason="not supported") def test_cpu_num(self): self.execute(self.proc.cpu_num) @@ -203,7 +202,7 @@ def test_memory_info(self): def test_memory_full_info(self): self.execute(self.proc.memory_full_info) - @unittest.skipIf(not POSIX, "POSIX only") + @pytest.mark.skipif(not POSIX, reason="POSIX only") @fewtimes_if_linux() def test_terminal(self): self.execute(self.proc.terminal) @@ -216,11 +215,11 @@ def test_resume(self): def test_cwd(self): self.execute(self.proc.cwd) - @unittest.skipIf(not HAS_CPU_AFFINITY, "not supported") + @pytest.mark.skipif(not HAS_CPU_AFFINITY, reason="not supported") def test_cpu_affinity(self): self.execute(self.proc.cpu_affinity) - @unittest.skipIf(not HAS_CPU_AFFINITY, "not supported") + @pytest.mark.skipif(not HAS_CPU_AFFINITY, reason="not supported") def test_cpu_affinity_set(self): affinity = thisproc.cpu_affinity() self.execute(lambda: self.proc.cpu_affinity(affinity)) @@ -231,18 +230,18 @@ def test_open_files(self): with open(get_testfn(), 'w'): self.execute(self.proc.open_files) - @unittest.skipIf(not HAS_MEMORY_MAPS, "not supported") + @pytest.mark.skipif(not HAS_MEMORY_MAPS, reason="not supported") @fewtimes_if_linux() def test_memory_maps(self): self.execute(self.proc.memory_maps) - @unittest.skipIf(not LINUX, "LINUX only") - @unittest.skipIf(not HAS_RLIMIT, "not supported") + @pytest.mark.skipif(not LINUX, reason="LINUX only") + @pytest.mark.skipif(not HAS_RLIMIT, reason="not supported") def test_rlimit(self): self.execute(lambda: self.proc.rlimit(psutil.RLIMIT_NOFILE)) - @unittest.skipIf(not LINUX, "LINUX only") - @unittest.skipIf(not HAS_RLIMIT, "not supported") + @pytest.mark.skipif(not LINUX, reason="LINUX only") + @pytest.mark.skipif(not HAS_RLIMIT, reason="not supported") def test_rlimit_set(self): limit = thisproc.rlimit(psutil.RLIMIT_NOFILE) self.execute(lambda: self.proc.rlimit(psutil.RLIMIT_NOFILE, limit)) @@ -251,7 +250,7 @@ def test_rlimit_set(self): @fewtimes_if_linux() # Windows implementation is based on a single system-wide # function (tested later). - @unittest.skipIf(WINDOWS, "worthless on WINDOWS") + @pytest.mark.skipif(WINDOWS, reason="worthless on WINDOWS") def test_net_connections(self): # TODO: UNIX sockets are temporarily implemented by parsing # 'pfiles' cmd output; we don't want that part of the code to @@ -260,11 +259,11 @@ def test_net_connections(self): kind = 'inet' if SUNOS else 'all' self.execute(lambda: self.proc.net_connections(kind)) - @unittest.skipIf(not HAS_ENVIRON, "not supported") + @pytest.mark.skipif(not HAS_ENVIRON, reason="not supported") def test_environ(self): self.execute(self.proc.environ) - @unittest.skipIf(not WINDOWS, "WINDOWS only") + @pytest.mark.skipif(not WINDOWS, reason="WINDOWS only") def test_proc_info(self): self.execute(lambda: cext.proc_info(os.getpid())) @@ -323,7 +322,7 @@ def call(): self.execute(call) -@unittest.skipIf(not WINDOWS, "WINDOWS only") +@pytest.mark.skipif(not WINDOWS, reason="WINDOWS only") class TestProcessDualImplementation(TestMemoryLeak): def test_cmdline_peb_true(self): self.execute(lambda: cext.proc_cmdline(os.getpid(), use_peb=True)) @@ -368,14 +367,14 @@ def test_cpu_stats(self): @fewtimes_if_linux() # TODO: remove this once 1892 is fixed - @unittest.skipIf( - MACOS and platform.machine() == 'arm64', "skipped due to #1892" + @pytest.mark.skipif( + MACOS and platform.machine() == 'arm64', reason="skipped due to #1892" ) - @unittest.skipIf(not HAS_CPU_FREQ, "not supported") + @pytest.mark.skipif(not HAS_CPU_FREQ, reason="not supported") def test_cpu_freq(self): self.execute(psutil.cpu_freq) - @unittest.skipIf(not WINDOWS, "WINDOWS only") + @pytest.mark.skipif(not WINDOWS, reason="WINDOWS only") def test_getloadavg(self): psutil.getloadavg() self.execute(psutil.getloadavg) @@ -386,7 +385,7 @@ def test_virtual_memory(self): self.execute(psutil.virtual_memory) # TODO: remove this skip when this gets fixed - @unittest.skipIf(SUNOS, "worthless on SUNOS (uses a subprocess)") + @pytest.mark.skipif(SUNOS, reason="worthless on SUNOS (uses a subprocess)") def test_swap_memory(self): self.execute(psutil.swap_memory) @@ -400,13 +399,13 @@ def test_disk_usage(self): times = FEW_TIMES if POSIX else self.times self.execute(lambda: psutil.disk_usage('.'), times=times) - @unittest.skipIf(QEMU_USER, "QEMU user not supported") + @pytest.mark.skipif(QEMU_USER, reason="QEMU user not supported") def test_disk_partitions(self): self.execute(psutil.disk_partitions) - @unittest.skipIf( + @pytest.mark.skipif( LINUX and not os.path.exists('/proc/diskstats'), - '/proc/diskstats not available on this Linux version', + reason="/proc/diskstats not available on this Linux version", ) @fewtimes_if_linux() def test_disk_io_counters(self): @@ -421,12 +420,12 @@ def test_pids(self): # --- net @fewtimes_if_linux() - @unittest.skipIf(not HAS_NET_IO_COUNTERS, 'not supported') + @pytest.mark.skipif(not HAS_NET_IO_COUNTERS, reason="not supported") def test_net_io_counters(self): self.execute(lambda: psutil.net_io_counters(nowrap=False)) @fewtimes_if_linux() - @unittest.skipIf(MACOS and os.getuid() != 0, "need root access") + @pytest.mark.skipif(MACOS and os.getuid() != 0, reason="need root access") def test_net_connections(self): # always opens and handle on Windows() (once) psutil.net_connections(kind='all') @@ -438,24 +437,24 @@ def test_net_if_addrs(self): tolerance = 80 * 1024 if WINDOWS else self.tolerance self.execute(psutil.net_if_addrs, tolerance=tolerance) - @unittest.skipIf(QEMU_USER, "QEMU user not supported") + @pytest.mark.skipif(QEMU_USER, reason="QEMU user not supported") def test_net_if_stats(self): self.execute(psutil.net_if_stats) # --- sensors @fewtimes_if_linux() - @unittest.skipIf(not HAS_SENSORS_BATTERY, "not supported") + @pytest.mark.skipif(not HAS_SENSORS_BATTERY, reason="not supported") def test_sensors_battery(self): self.execute(psutil.sensors_battery) @fewtimes_if_linux() - @unittest.skipIf(not HAS_SENSORS_TEMPERATURES, "not supported") + @pytest.mark.skipif(not HAS_SENSORS_TEMPERATURES, reason="not supported") def test_sensors_temperatures(self): self.execute(psutil.sensors_temperatures) @fewtimes_if_linux() - @unittest.skipIf(not HAS_SENSORS_FANS, "not supported") + @pytest.mark.skipif(not HAS_SENSORS_FANS, reason="not supported") def test_sensors_fans(self): self.execute(psutil.sensors_fans) @@ -492,14 +491,3 @@ def test_win_service_get_status(self): def test_win_service_get_description(self): name = next(psutil.win_service_iter()).name() self.execute(lambda: cext.winservice_query_descr(name)) - - -if __name__ == '__main__': - from psutil.tests.runner import cprint - from psutil.tests.runner import run_from_name - - if QEMU_USER: - cprint("skipping %s tests under QEMU_USER" % __file__, "brown") - sys.exit(0) - - run_from_name(__file__) diff --git a/psutil/tests/test_misc.py b/psutil/tests/test_misc.py index f341c930e..ca189a97b 100755 --- a/psutil/tests/test_misc.py +++ b/psutil/tests/test_misc.py @@ -16,7 +16,6 @@ import socket import stat import sys -import unittest import psutil import psutil.tests @@ -48,6 +47,7 @@ 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 from psutil.tests import sh from psutil.tests import system_namespace @@ -60,26 +60,24 @@ class TestSpecialMethods(PsutilTestCase): def test_check_pid_range(self): - with self.assertRaises(OverflowError): + with pytest.raises(OverflowError): psutil._psplatform.cext.check_pid_range(2**128) - with self.assertRaises(psutil.NoSuchProcess): + with pytest.raises(psutil.NoSuchProcess): psutil.Process(2**128) def test_process__repr__(self, func=repr): p = psutil.Process(self.spawn_testproc().pid) r = func(p) - self.assertIn("psutil.Process", r) - self.assertIn("pid=%s" % p.pid, r) - self.assertIn( - "name='%s'" % str(p.name()), r.replace("name=u'", "name='") - ) - self.assertIn("status=", r) - self.assertNotIn("exitcode=", r) + assert "psutil.Process" in r + assert "pid=%s" % p.pid in r + assert "name='%s'" % str(p.name()) in r.replace("name=u'", "name='") + assert "status=" in r + assert "exitcode=" not in r p.terminate() p.wait() r = func(p) - self.assertIn("status='terminated'", r) - self.assertIn("exitcode=", r) + assert "status='terminated'" in r + assert "exitcode=" in r with mock.patch.object( psutil.Process, @@ -88,9 +86,9 @@ def test_process__repr__(self, func=repr): ): p = psutil.Process() r = func(p) - self.assertIn("pid=%s" % p.pid, r) - self.assertIn("status='zombie'", r) - self.assertNotIn("name=", r) + assert "pid=%s" % p.pid in r + assert "status='zombie'" in r + assert "name=" not in r with mock.patch.object( psutil.Process, "name", @@ -98,9 +96,9 @@ def test_process__repr__(self, func=repr): ): p = psutil.Process() r = func(p) - self.assertIn("pid=%s" % p.pid, r) - self.assertIn("terminated", r) - self.assertNotIn("name=", r) + assert "pid=%s" % p.pid in r + assert "terminated" in r + assert "name=" not in r with mock.patch.object( psutil.Process, "name", @@ -108,106 +106,104 @@ def test_process__repr__(self, func=repr): ): p = psutil.Process() r = func(p) - self.assertIn("pid=%s" % p.pid, r) - self.assertNotIn("name=", r) + assert "pid=%s" % p.pid in r + assert "name=" not in r def test_process__str__(self): self.test_process__repr__(func=str) def test_error__repr__(self): - self.assertEqual(repr(psutil.Error()), "psutil.Error()") + assert repr(psutil.Error()) == "psutil.Error()" def test_error__str__(self): - self.assertEqual(str(psutil.Error()), "") + assert str(psutil.Error()) == "" # noqa def test_no_such_process__repr__(self): - self.assertEqual( - repr(psutil.NoSuchProcess(321)), - "psutil.NoSuchProcess(pid=321, msg='process no longer exists')", + assert ( + repr(psutil.NoSuchProcess(321)) + == "psutil.NoSuchProcess(pid=321, msg='process no longer exists')" ) - self.assertEqual( - repr(psutil.NoSuchProcess(321, name="name", msg="msg")), - "psutil.NoSuchProcess(pid=321, name='name', msg='msg')", + assert ( + repr(psutil.NoSuchProcess(321, name="name", msg="msg")) + == "psutil.NoSuchProcess(pid=321, name='name', msg='msg')" ) def test_no_such_process__str__(self): - self.assertEqual( - str(psutil.NoSuchProcess(321)), - "process no longer exists (pid=321)", + assert ( + str(psutil.NoSuchProcess(321)) + == "process no longer exists (pid=321)" ) - self.assertEqual( - str(psutil.NoSuchProcess(321, name="name", msg="msg")), - "msg (pid=321, name='name')", + assert ( + str(psutil.NoSuchProcess(321, name="name", msg="msg")) + == "msg (pid=321, name='name')" ) def test_zombie_process__repr__(self): - self.assertEqual( - repr(psutil.ZombieProcess(321)), - 'psutil.ZombieProcess(pid=321, msg="PID still ' - 'exists but it\'s a zombie")', + assert ( + repr(psutil.ZombieProcess(321)) + == 'psutil.ZombieProcess(pid=321, msg="PID still ' + 'exists but it\'s a zombie")' ) - self.assertEqual( - repr(psutil.ZombieProcess(321, name="name", ppid=320, msg="foo")), - "psutil.ZombieProcess(pid=321, ppid=320, name='name', msg='foo')", + assert ( + repr(psutil.ZombieProcess(321, name="name", ppid=320, msg="foo")) + == "psutil.ZombieProcess(pid=321, ppid=320, name='name'," + " msg='foo')" ) def test_zombie_process__str__(self): - self.assertEqual( - str(psutil.ZombieProcess(321)), - "PID still exists but it's a zombie (pid=321)", + assert ( + str(psutil.ZombieProcess(321)) + == "PID still exists but it's a zombie (pid=321)" ) - self.assertEqual( - str(psutil.ZombieProcess(321, name="name", ppid=320, msg="foo")), - "foo (pid=321, ppid=320, name='name')", + assert ( + str(psutil.ZombieProcess(321, name="name", ppid=320, msg="foo")) + == "foo (pid=321, ppid=320, name='name')" ) def test_access_denied__repr__(self): - self.assertEqual( - repr(psutil.AccessDenied(321)), "psutil.AccessDenied(pid=321)" - ) - self.assertEqual( - repr(psutil.AccessDenied(321, name="name", msg="msg")), - "psutil.AccessDenied(pid=321, name='name', msg='msg')", + assert repr(psutil.AccessDenied(321)) == "psutil.AccessDenied(pid=321)" + assert ( + repr(psutil.AccessDenied(321, name="name", msg="msg")) + == "psutil.AccessDenied(pid=321, name='name', msg='msg')" ) def test_access_denied__str__(self): - self.assertEqual(str(psutil.AccessDenied(321)), "(pid=321)") - self.assertEqual( - str(psutil.AccessDenied(321, name="name", msg="msg")), - "msg (pid=321, name='name')", + assert str(psutil.AccessDenied(321)) == "(pid=321)" + assert ( + str(psutil.AccessDenied(321, name="name", msg="msg")) + == "msg (pid=321, name='name')" ) def test_timeout_expired__repr__(self): - self.assertEqual( - repr(psutil.TimeoutExpired(5)), - "psutil.TimeoutExpired(seconds=5, msg='timeout after 5 seconds')", + assert ( + repr(psutil.TimeoutExpired(5)) + == "psutil.TimeoutExpired(seconds=5, msg='timeout after 5" + " seconds')" ) - self.assertEqual( - repr(psutil.TimeoutExpired(5, pid=321, name="name")), - "psutil.TimeoutExpired(pid=321, name='name', seconds=5, " - "msg='timeout after 5 seconds')", + assert ( + repr(psutil.TimeoutExpired(5, pid=321, name="name")) + == "psutil.TimeoutExpired(pid=321, name='name', seconds=5, " + "msg='timeout after 5 seconds')" ) def test_timeout_expired__str__(self): - self.assertEqual( - str(psutil.TimeoutExpired(5)), "timeout after 5 seconds" - ) - self.assertEqual( - str(psutil.TimeoutExpired(5, pid=321, name="name")), - "timeout after 5 seconds (pid=321, name='name')", + assert str(psutil.TimeoutExpired(5)) == "timeout after 5 seconds" + assert ( + str(psutil.TimeoutExpired(5, pid=321, name="name")) + == "timeout after 5 seconds (pid=321, name='name')" ) def test_process__eq__(self): p1 = psutil.Process() p2 = psutil.Process() - self.assertEqual(p1, p2) + assert p1 == p2 p2._ident = (0, 0) - self.assertNotEqual(p1, p2) - self.assertNotEqual(p1, 'foo') + assert p1 != p2 + assert p1 != 'foo' def test_process__hash__(self): s = set([psutil.Process(), psutil.Process()]) - self.assertEqual(len(s), 1) + assert len(s) == 1 # =================================================================== @@ -247,18 +243,19 @@ def test__all__(self): # Can't do `from psutil import *` as it won't work on python 3 # so we simply iterate over __all__. for name in psutil.__all__: - self.assertIn(name, dir_psutil) + assert name in dir_psutil def test_version(self): - self.assertEqual( - '.'.join([str(x) for x in psutil.version_info]), psutil.__version__ + assert ( + '.'.join([str(x) for x in psutil.version_info]) + == psutil.__version__ ) def test_process_as_dict_no_new_names(self): # See https://github.com/giampaolo/psutil/issues/813 p = psutil.Process() p.foo = '1' - self.assertNotIn('foo', p.as_dict()) + assert 'foo' not in p.as_dict() def test_serialization(self): def check(ret): @@ -266,7 +263,7 @@ def check(ret): a = pickle.dumps(ret) b = pickle.loads(a) - self.assertEqual(ret, b) + assert ret == b # --- process APIs @@ -307,46 +304,48 @@ def check(ret): psutil.NoSuchProcess(pid=4567, name='name', msg='msg') ) ) - self.assertIsInstance(b, psutil.NoSuchProcess) - self.assertEqual(b.pid, 4567) - self.assertEqual(b.name, 'name') - self.assertEqual(b.msg, 'msg') + assert isinstance(b, psutil.NoSuchProcess) + assert b.pid == 4567 + assert b.name == 'name' + assert b.msg == 'msg' b = pickle.loads( pickle.dumps( psutil.ZombieProcess(pid=4567, name='name', ppid=42, msg='msg') ) ) - self.assertIsInstance(b, psutil.ZombieProcess) - self.assertEqual(b.pid, 4567) - self.assertEqual(b.ppid, 42) - self.assertEqual(b.name, 'name') - self.assertEqual(b.msg, 'msg') + assert isinstance(b, psutil.ZombieProcess) + assert b.pid == 4567 + assert b.ppid == 42 + assert b.name == 'name' + assert b.msg == 'msg' b = pickle.loads( pickle.dumps(psutil.AccessDenied(pid=123, name='name', msg='msg')) ) - self.assertIsInstance(b, psutil.AccessDenied) - self.assertEqual(b.pid, 123) - self.assertEqual(b.name, 'name') - self.assertEqual(b.msg, 'msg') + assert isinstance(b, psutil.AccessDenied) + assert b.pid == 123 + assert b.name == 'name' + assert b.msg == 'msg' b = pickle.loads( pickle.dumps( psutil.TimeoutExpired(seconds=33, pid=4567, name='name') ) ) - self.assertIsInstance(b, psutil.TimeoutExpired) - self.assertEqual(b.seconds, 33) - self.assertEqual(b.pid, 4567) - self.assertEqual(b.name, 'name') + assert isinstance(b, psutil.TimeoutExpired) + assert b.seconds == 33 + assert b.pid == 4567 + assert b.name == 'name' # # XXX: https://github.com/pypa/setuptools/pull/2896 - # @unittest.skipIf(APPVEYOR, "temporarily disabled due to setuptools bug") + # @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): - # raise unittest.SkipTest("can't find setup.py") + # raise pytest.skip("can't find setup.py") # module = import_module_by_path(setup_py) # self.assertRaises(SystemExit, module.setup) # self.assertEqual(module.get_version(), psutil.__version__) @@ -369,7 +368,7 @@ def test_ad_on_process_creation(self): with mock.patch.object( psutil.Process, '_get_ident', side_effect=ValueError ) as meth: - with self.assertRaises(ValueError): + with pytest.raises(ValueError): psutil.Process() assert meth.called @@ -385,9 +384,9 @@ def test_sanity_version_check(self): with mock.patch( "psutil._psplatform.cext.version", return_value="0.0.0" ): - with self.assertRaises(ImportError) as cm: + with pytest.raises(ImportError) as cm: reload_module(psutil) - self.assertIn("version conflict", str(cm.exception).lower()) + assert "version conflict" in str(cm.value).lower() # =================================================================== @@ -405,32 +404,30 @@ def run_against(self, obj, expected_retval=None): # no args for _ in range(2): ret = obj() - self.assertEqual(self.calls, [((), {})]) + assert self.calls == [((), {})] if expected_retval is not None: - self.assertEqual(ret, expected_retval) + assert ret == expected_retval # with args for _ in range(2): ret = obj(1) - self.assertEqual(self.calls, [((), {}), ((1,), {})]) + assert self.calls == [((), {}), ((1,), {})] if expected_retval is not None: - self.assertEqual(ret, expected_retval) + assert ret == expected_retval # with args + kwargs for _ in range(2): ret = obj(1, bar=2) - self.assertEqual( - self.calls, [((), {}), ((1,), {}), ((1,), {'bar': 2})] - ) + assert self.calls == [((), {}), ((1,), {}), ((1,), {'bar': 2})] if expected_retval is not None: - self.assertEqual(ret, expected_retval) + assert ret == expected_retval # clear cache - self.assertEqual(len(self.calls), 3) + assert len(self.calls) == 3 obj.cache_clear() ret = obj() if expected_retval is not None: - self.assertEqual(ret, expected_retval) - self.assertEqual(len(self.calls), 4) + assert ret == expected_retval + assert len(self.calls) == 4 # docstring - self.assertEqual(obj.__doc__, "My docstring.") + assert obj.__doc__ == "My docstring." def test_function(self): @memoize @@ -455,7 +452,7 @@ def bar(self): baseclass = self self.run_against(Foo, expected_retval=None) - self.assertEqual(Foo().bar(), 22) + assert Foo().bar() == 22 def test_class_singleton(self): # @memoize can be used against classes to create singletons @@ -464,11 +461,11 @@ class Bar: def __init__(self, *args, **kwargs): pass - self.assertIs(Bar(), Bar()) - self.assertEqual(id(Bar()), id(Bar())) - self.assertEqual(id(Bar(1)), id(Bar(1))) - self.assertEqual(id(Bar(1, foo=3)), id(Bar(1, foo=3))) - self.assertNotEqual(id(Bar(1)), id(Bar(2))) + assert Bar() is Bar() + assert id(Bar()) == id(Bar()) + assert id(Bar(1)) == id(Bar(1)) + assert id(Bar(1, foo=3)) == id(Bar(1, foo=3)) + assert id(Bar(1)) != id(Bar(2)) def test_staticmethod(self): class Foo: @@ -508,28 +505,28 @@ def foo(*args, **kwargs): for _ in range(2): ret = foo() expected = ((), {}) - self.assertEqual(ret, expected) - self.assertEqual(len(calls), 1) + assert ret == expected + assert len(calls) == 1 # with args for _ in range(2): ret = foo(1) expected = ((1,), {}) - self.assertEqual(ret, expected) - self.assertEqual(len(calls), 2) + assert ret == expected + assert len(calls) == 2 # with args + kwargs for _ in range(2): ret = foo(1, bar=2) expected = ((1,), {'bar': 2}) - self.assertEqual(ret, expected) - self.assertEqual(len(calls), 3) + assert ret == expected + assert len(calls) == 3 # clear cache foo.cache_clear() ret = foo() expected = ((), {}) - self.assertEqual(ret, expected) - self.assertEqual(len(calls), 4) + assert ret == expected + assert len(calls) == 4 # docstring - self.assertEqual(foo.__doc__, "Foo docstring.") + assert foo.__doc__ == "Foo docstring." class TestCommonModule(PsutilTestCase): @@ -543,43 +540,42 @@ def foo(self): calls = [] f.foo() f.foo() - self.assertEqual(len(calls), 2) + assert len(calls) == 2 # activate calls = [] f.foo.cache_activate(f) f.foo() f.foo() - self.assertEqual(len(calls), 1) + assert len(calls) == 1 # deactivate calls = [] f.foo.cache_deactivate(f) f.foo() f.foo() - self.assertEqual(len(calls), 2) + assert len(calls) == 2 def test_parse_environ_block(self): def k(s): return s.upper() if WINDOWS else s - self.assertEqual(parse_environ_block("a=1\0"), {k("a"): "1"}) - self.assertEqual( - parse_environ_block("a=1\0b=2\0\0"), {k("a"): "1", k("b"): "2"} - ) - self.assertEqual( - parse_environ_block("a=1\0b=\0\0"), {k("a"): "1", k("b"): ""} - ) + assert parse_environ_block("a=1\0") == {k("a"): "1"} + assert parse_environ_block("a=1\0b=2\0\0") == { + k("a"): "1", + k("b"): "2", + } + assert parse_environ_block("a=1\0b=\0\0") == {k("a"): "1", k("b"): ""} # ignore everything after \0\0 - self.assertEqual( - parse_environ_block("a=1\0b=2\0\0c=3\0"), - {k("a"): "1", k("b"): "2"}, - ) + assert parse_environ_block("a=1\0b=2\0\0c=3\0") == { + k("a"): "1", + k("b"): "2", + } # ignore everything that is not an assignment - self.assertEqual(parse_environ_block("xxx\0a=1\0"), {k("a"): "1"}) - self.assertEqual(parse_environ_block("a=1\0=b=2\0"), {k("a"): "1"}) + assert parse_environ_block("xxx\0a=1\0") == {k("a"): "1"} + assert parse_environ_block("a=1\0=b=2\0") == {k("a"): "1"} # do not fail if the block is incomplete - self.assertEqual(parse_environ_block("a=1\0b=2"), {k("a"): "1"}) + assert parse_environ_block("a=1\0b=2") == {k("a"): "1"} def test_supports_ipv6(self): self.addCleanup(supports_ipv6.cache_clear) @@ -613,7 +609,7 @@ def test_supports_ipv6(self): supports_ipv6.cache_clear() assert s.called else: - with self.assertRaises(socket.error): + with pytest.raises(socket.error): sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) try: sock.bind(("::1", 0)) @@ -627,11 +623,13 @@ def test_isfile_strict(self): with mock.patch( 'psutil._common.os.stat', side_effect=OSError(errno.EPERM, "foo") ): - self.assertRaises(OSError, isfile_strict, this_file) + with pytest.raises(OSError): + isfile_strict(this_file) with mock.patch( 'psutil._common.os.stat', side_effect=OSError(errno.EACCES, "foo") ): - self.assertRaises(OSError, isfile_strict, this_file) + with pytest.raises(OSError): + isfile_strict(this_file) with mock.patch( 'psutil._common.os.stat', side_effect=OSError(errno.ENOENT, "foo") ): @@ -645,40 +643,45 @@ def test_debug(self): else: from StringIO import StringIO - with redirect_stderr(StringIO()) as f: - debug("hello") - sys.stderr.flush() + with mock.patch.object(psutil._common, "PSUTIL_DEBUG", True): + with redirect_stderr(StringIO()) as f: + debug("hello") + sys.stderr.flush() msg = f.getvalue() assert msg.startswith("psutil-debug"), msg - self.assertIn("hello", msg) - self.assertIn(__file__.replace('.pyc', '.py'), msg) + assert "hello" in msg + assert __file__.replace('.pyc', '.py') in msg # supposed to use repr(exc) - with redirect_stderr(StringIO()) as f: - debug(ValueError("this is an error")) + with mock.patch.object(psutil._common, "PSUTIL_DEBUG", True): + with redirect_stderr(StringIO()) as f: + debug(ValueError("this is an error")) msg = f.getvalue() - self.assertIn("ignoring ValueError", msg) - self.assertIn("'this is an error'", msg) + assert "ignoring ValueError" in msg + assert "'this is an error'" in msg # supposed to use str(exc), because of extra info about file name - with redirect_stderr(StringIO()) as f: - exc = OSError(2, "no such file") - exc.filename = "/foo" - debug(exc) + with mock.patch.object(psutil._common, "PSUTIL_DEBUG", True): + with redirect_stderr(StringIO()) as f: + exc = OSError(2, "no such file") + exc.filename = "/foo" + debug(exc) msg = f.getvalue() - self.assertIn("no such file", msg) - self.assertIn("/foo", msg) + assert "no such file" in msg + assert "/foo" in msg def test_cat_bcat(self): testfn = self.get_testfn() with open(testfn, "w") as f: f.write("foo") - self.assertEqual(cat(testfn), "foo") - self.assertEqual(bcat(testfn), b"foo") - self.assertRaises(FileNotFoundError, cat, testfn + '-invalid') - self.assertRaises(FileNotFoundError, bcat, testfn + '-invalid') - self.assertEqual(cat(testfn + '-invalid', fallback="bar"), "bar") - self.assertEqual(bcat(testfn + '-invalid', fallback="bar"), "bar") + assert cat(testfn) == "foo" + assert bcat(testfn) == b"foo" + with pytest.raises(FileNotFoundError): + cat(testfn + '-invalid') + with pytest.raises(FileNotFoundError): + bcat(testfn + '-invalid') + assert cat(testfn + '-invalid', fallback="bar") == "bar" + assert bcat(testfn + '-invalid', fallback="bar") == "bar" # =================================================================== @@ -697,105 +700,89 @@ def setUp(self): def test_first_call(self): input = {'disk1': nt(5, 5, 5)} - self.assertEqual(wrap_numbers(input, 'disk_io'), input) + assert wrap_numbers(input, 'disk_io') == input def test_input_hasnt_changed(self): input = {'disk1': nt(5, 5, 5)} - self.assertEqual(wrap_numbers(input, 'disk_io'), input) - self.assertEqual(wrap_numbers(input, 'disk_io'), input) + assert wrap_numbers(input, 'disk_io') == input + assert wrap_numbers(input, 'disk_io') == input def test_increase_but_no_wrap(self): input = {'disk1': nt(5, 5, 5)} - self.assertEqual(wrap_numbers(input, 'disk_io'), input) + assert wrap_numbers(input, 'disk_io') == input input = {'disk1': nt(10, 15, 20)} - self.assertEqual(wrap_numbers(input, 'disk_io'), input) + assert wrap_numbers(input, 'disk_io') == input input = {'disk1': nt(20, 25, 30)} - self.assertEqual(wrap_numbers(input, 'disk_io'), input) + assert wrap_numbers(input, 'disk_io') == input input = {'disk1': nt(20, 25, 30)} - self.assertEqual(wrap_numbers(input, 'disk_io'), input) + assert wrap_numbers(input, 'disk_io') == input def test_wrap(self): # let's say 100 is the threshold input = {'disk1': nt(100, 100, 100)} - self.assertEqual(wrap_numbers(input, 'disk_io'), input) + assert wrap_numbers(input, 'disk_io') == input # first wrap restarts from 10 input = {'disk1': nt(100, 100, 10)} - self.assertEqual( - wrap_numbers(input, 'disk_io'), {'disk1': nt(100, 100, 110)} - ) + assert wrap_numbers(input, 'disk_io') == {'disk1': nt(100, 100, 110)} # then it remains the same input = {'disk1': nt(100, 100, 10)} - self.assertEqual( - wrap_numbers(input, 'disk_io'), {'disk1': nt(100, 100, 110)} - ) + assert wrap_numbers(input, 'disk_io') == {'disk1': nt(100, 100, 110)} # then it goes up input = {'disk1': nt(100, 100, 90)} - self.assertEqual( - wrap_numbers(input, 'disk_io'), {'disk1': nt(100, 100, 190)} - ) + assert wrap_numbers(input, 'disk_io') == {'disk1': nt(100, 100, 190)} # then it wraps again input = {'disk1': nt(100, 100, 20)} - self.assertEqual( - wrap_numbers(input, 'disk_io'), {'disk1': nt(100, 100, 210)} - ) + assert wrap_numbers(input, 'disk_io') == {'disk1': nt(100, 100, 210)} # and remains the same input = {'disk1': nt(100, 100, 20)} - self.assertEqual( - wrap_numbers(input, 'disk_io'), {'disk1': nt(100, 100, 210)} - ) + assert wrap_numbers(input, 'disk_io') == {'disk1': nt(100, 100, 210)} # now wrap another num input = {'disk1': nt(50, 100, 20)} - self.assertEqual( - wrap_numbers(input, 'disk_io'), {'disk1': nt(150, 100, 210)} - ) + assert wrap_numbers(input, 'disk_io') == {'disk1': nt(150, 100, 210)} # and again input = {'disk1': nt(40, 100, 20)} - self.assertEqual( - wrap_numbers(input, 'disk_io'), {'disk1': nt(190, 100, 210)} - ) + assert wrap_numbers(input, 'disk_io') == {'disk1': nt(190, 100, 210)} # keep it the same input = {'disk1': nt(40, 100, 20)} - self.assertEqual( - wrap_numbers(input, 'disk_io'), {'disk1': nt(190, 100, 210)} - ) + assert wrap_numbers(input, 'disk_io') == {'disk1': nt(190, 100, 210)} def test_changing_keys(self): # Emulate a case where the second call to disk_io() # (or whatever) provides a new disk, then the new disk # disappears on the third call. input = {'disk1': nt(5, 5, 5)} - self.assertEqual(wrap_numbers(input, 'disk_io'), input) + assert wrap_numbers(input, 'disk_io') == input input = {'disk1': nt(5, 5, 5), 'disk2': nt(7, 7, 7)} - self.assertEqual(wrap_numbers(input, 'disk_io'), input) + assert wrap_numbers(input, 'disk_io') == input input = {'disk1': nt(8, 8, 8)} - self.assertEqual(wrap_numbers(input, 'disk_io'), input) + assert wrap_numbers(input, 'disk_io') == input def test_changing_keys_w_wrap(self): input = {'disk1': nt(50, 50, 50), 'disk2': nt(100, 100, 100)} - self.assertEqual(wrap_numbers(input, 'disk_io'), input) + assert wrap_numbers(input, 'disk_io') == input # disk 2 wraps input = {'disk1': nt(50, 50, 50), 'disk2': nt(100, 100, 10)} - self.assertEqual( - wrap_numbers(input, 'disk_io'), - {'disk1': nt(50, 50, 50), 'disk2': nt(100, 100, 110)}, - ) + assert wrap_numbers(input, 'disk_io') == { + 'disk1': nt(50, 50, 50), + 'disk2': nt(100, 100, 110), + } # disk 2 disappears input = {'disk1': nt(50, 50, 50)} - self.assertEqual(wrap_numbers(input, 'disk_io'), input) + assert wrap_numbers(input, 'disk_io') == input # then it appears again; the old wrap is supposed to be # gone. input = {'disk1': nt(50, 50, 50), 'disk2': nt(100, 100, 100)} - self.assertEqual(wrap_numbers(input, 'disk_io'), input) + assert wrap_numbers(input, 'disk_io') == input # remains the same input = {'disk1': nt(50, 50, 50), 'disk2': nt(100, 100, 100)} - self.assertEqual(wrap_numbers(input, 'disk_io'), input) + assert wrap_numbers(input, 'disk_io') == input # and then wraps again input = {'disk1': nt(50, 50, 50), 'disk2': nt(100, 100, 10)} - self.assertEqual( - wrap_numbers(input, 'disk_io'), - {'disk1': nt(50, 50, 50), 'disk2': nt(100, 100, 110)}, - ) + assert wrap_numbers(input, 'disk_io') == { + 'disk1': nt(50, 50, 50), + 'disk2': nt(100, 100, 110), + } def test_real_data(self): d = { @@ -804,8 +791,8 @@ def test_real_data(self): 'nvme0n1p2': (54, 54, 2396160, 5165056, 4, 24, 30, 1207, 28), 'nvme0n1p3': (2389, 4539, 5154, 150, 4828, 1844, 2019, 398, 348), } - self.assertEqual(wrap_numbers(d, 'disk_io'), d) - self.assertEqual(wrap_numbers(d, 'disk_io'), d) + assert wrap_numbers(d, 'disk_io') == d + assert wrap_numbers(d, 'disk_io') == d # decrease this โ†“ d = { 'nvme0n1': (100, 508, 640, 1571, 5970, 1987, 2049, 451751, 47048), @@ -814,7 +801,7 @@ def test_real_data(self): 'nvme0n1p3': (2389, 4539, 5154, 150, 4828, 1844, 2019, 398, 348), } out = wrap_numbers(d, 'disk_io') - self.assertEqual(out['nvme0n1'][0], 400) + assert out['nvme0n1'][0] == 400 # --- cache tests @@ -822,9 +809,9 @@ def test_cache_first_call(self): input = {'disk1': nt(5, 5, 5)} wrap_numbers(input, 'disk_io') cache = wrap_numbers.cache_info() - self.assertEqual(cache[0], {'disk_io': input}) - self.assertEqual(cache[1], {'disk_io': {}}) - self.assertEqual(cache[2], {'disk_io': {}}) + assert cache[0] == {'disk_io': input} + assert cache[1] == {'disk_io': {}} + assert cache[2] == {'disk_io': {}} def test_cache_call_twice(self): input = {'disk1': nt(5, 5, 5)} @@ -832,12 +819,11 @@ def test_cache_call_twice(self): input = {'disk1': nt(10, 10, 10)} wrap_numbers(input, 'disk_io') cache = wrap_numbers.cache_info() - self.assertEqual(cache[0], {'disk_io': input}) - self.assertEqual( - cache[1], - {'disk_io': {('disk1', 0): 0, ('disk1', 1): 0, ('disk1', 2): 0}}, - ) - self.assertEqual(cache[2], {'disk_io': {}}) + assert cache[0] == {'disk_io': input} + assert cache[1] == { + 'disk_io': {('disk1', 0): 0, ('disk1', 1): 0, ('disk1', 2): 0} + } + assert cache[2] == {'disk_io': {}} def test_cache_wrap(self): # let's say 100 is the threshold @@ -848,53 +834,46 @@ def test_cache_wrap(self): input = {'disk1': nt(100, 100, 10)} wrap_numbers(input, 'disk_io') cache = wrap_numbers.cache_info() - self.assertEqual(cache[0], {'disk_io': input}) - self.assertEqual( - cache[1], - {'disk_io': {('disk1', 0): 0, ('disk1', 1): 0, ('disk1', 2): 100}}, - ) - self.assertEqual(cache[2], {'disk_io': {'disk1': set([('disk1', 2)])}}) + assert cache[0] == {'disk_io': input} + assert cache[1] == { + 'disk_io': {('disk1', 0): 0, ('disk1', 1): 0, ('disk1', 2): 100} + } + assert cache[2] == {'disk_io': {'disk1': set([('disk1', 2)])}} def check_cache_info(): cache = wrap_numbers.cache_info() - self.assertEqual( - cache[1], - { - 'disk_io': { - ('disk1', 0): 0, - ('disk1', 1): 0, - ('disk1', 2): 100, - } - }, - ) - self.assertEqual( - cache[2], {'disk_io': {'disk1': set([('disk1', 2)])}} - ) + assert cache[1] == { + 'disk_io': { + ('disk1', 0): 0, + ('disk1', 1): 0, + ('disk1', 2): 100, + } + } + assert cache[2] == {'disk_io': {'disk1': set([('disk1', 2)])}} # then it remains the same input = {'disk1': nt(100, 100, 10)} wrap_numbers(input, 'disk_io') cache = wrap_numbers.cache_info() - self.assertEqual(cache[0], {'disk_io': input}) + assert cache[0] == {'disk_io': input} check_cache_info() # then it goes up input = {'disk1': nt(100, 100, 90)} wrap_numbers(input, 'disk_io') cache = wrap_numbers.cache_info() - self.assertEqual(cache[0], {'disk_io': input}) + assert cache[0] == {'disk_io': input} check_cache_info() # then it wraps again input = {'disk1': nt(100, 100, 20)} wrap_numbers(input, 'disk_io') cache = wrap_numbers.cache_info() - self.assertEqual(cache[0], {'disk_io': input}) - self.assertEqual( - cache[1], - {'disk_io': {('disk1', 0): 0, ('disk1', 1): 0, ('disk1', 2): 190}}, - ) - self.assertEqual(cache[2], {'disk_io': {'disk1': set([('disk1', 2)])}}) + assert cache[0] == {'disk_io': input} + assert cache[1] == { + 'disk_io': {('disk1', 0): 0, ('disk1', 1): 0, ('disk1', 2): 190} + } + assert cache[2] == {'disk_io': {'disk1': set([('disk1', 2)])}} def test_cache_changing_keys(self): input = {'disk1': nt(5, 5, 5)} @@ -902,42 +881,41 @@ def test_cache_changing_keys(self): input = {'disk1': nt(5, 5, 5), 'disk2': nt(7, 7, 7)} wrap_numbers(input, 'disk_io') cache = wrap_numbers.cache_info() - self.assertEqual(cache[0], {'disk_io': input}) - self.assertEqual( - cache[1], - {'disk_io': {('disk1', 0): 0, ('disk1', 1): 0, ('disk1', 2): 0}}, - ) - self.assertEqual(cache[2], {'disk_io': {}}) + assert cache[0] == {'disk_io': input} + assert cache[1] == { + 'disk_io': {('disk1', 0): 0, ('disk1', 1): 0, ('disk1', 2): 0} + } + assert cache[2] == {'disk_io': {}} def test_cache_clear(self): input = {'disk1': nt(5, 5, 5)} wrap_numbers(input, 'disk_io') wrap_numbers(input, 'disk_io') wrap_numbers.cache_clear('disk_io') - self.assertEqual(wrap_numbers.cache_info(), ({}, {}, {})) + assert wrap_numbers.cache_info() == ({}, {}, {}) wrap_numbers.cache_clear('disk_io') wrap_numbers.cache_clear('?!?') - @unittest.skipIf(not HAS_NET_IO_COUNTERS, 'not supported') + @pytest.mark.skipif(not HAS_NET_IO_COUNTERS, reason="not supported") def test_cache_clear_public_apis(self): if not psutil.disk_io_counters() or not psutil.net_io_counters(): - raise unittest.SkipTest("no disks or NICs available") + raise pytest.skip("no disks or NICs available") psutil.disk_io_counters() psutil.net_io_counters() caches = wrap_numbers.cache_info() for cache in caches: - self.assertIn('psutil.disk_io_counters', cache) - self.assertIn('psutil.net_io_counters', cache) + assert 'psutil.disk_io_counters' in cache + assert 'psutil.net_io_counters' in cache psutil.disk_io_counters.cache_clear() caches = wrap_numbers.cache_info() for cache in caches: - self.assertIn('psutil.net_io_counters', cache) - self.assertNotIn('psutil.disk_io_counters', cache) + assert 'psutil.net_io_counters' in cache + assert 'psutil.disk_io_counters' not in cache psutil.net_io_counters.cache_clear() caches = wrap_numbers.cache_info() - self.assertEqual(caches, ({}, {}, {})) + assert caches == ({}, {}, {}) # =================================================================== @@ -945,8 +923,8 @@ def test_cache_clear_public_apis(self): # =================================================================== -@unittest.skipIf( - not os.path.exists(SCRIPTS_DIR), "can't locate scripts directory" +@pytest.mark.skipif( + not os.path.exists(SCRIPTS_DIR), reason="can't locate scripts directory" ) class TestScripts(PsutilTestCase): """Tests for scripts in the "scripts" directory.""" @@ -987,7 +965,7 @@ def test_coverage(self): % os.path.join(SCRIPTS_DIR, name) ) - @unittest.skipIf(not POSIX, "POSIX only") + @pytest.mark.skipif(not POSIX, reason="POSIX only") def test_executable(self): for root, dirs, files in os.walk(SCRIPTS_DIR): for file in files: @@ -1008,7 +986,7 @@ def test_meminfo(self): def test_procinfo(self): self.assert_stdout('procinfo.py', str(os.getpid())) - @unittest.skipIf(CI_TESTING and not psutil.users(), "no users") + @pytest.mark.skipif(CI_TESTING and not psutil.users(), reason="no users") def test_who(self): self.assert_stdout('who.py') @@ -1021,17 +999,17 @@ def test_pstree(self): def test_netstat(self): self.assert_stdout('netstat.py') - @unittest.skipIf(QEMU_USER, 'QEMU user not supported') + @pytest.mark.skipif(QEMU_USER, reason="QEMU user not supported") def test_ifconfig(self): self.assert_stdout('ifconfig.py') - @unittest.skipIf(not HAS_MEMORY_MAPS, "not supported") + @pytest.mark.skipif(not HAS_MEMORY_MAPS, reason="not supported") def test_pmap(self): self.assert_stdout('pmap.py', str(os.getpid())) def test_procsmem(self): if 'uss' not in psutil.Process().memory_full_info()._fields: - raise unittest.SkipTest("not supported") + raise pytest.skip("not supported") self.assert_stdout('procsmem.py') def test_killall(self): @@ -1048,39 +1026,33 @@ def test_iotop(self): def test_pidof(self): output = self.assert_stdout('pidof.py', psutil.Process().name()) - self.assertIn(str(os.getpid()), output) + assert str(os.getpid()) in output - @unittest.skipIf(not WINDOWS, "WINDOWS only") + @pytest.mark.skipif(not WINDOWS, reason="WINDOWS only") def test_winservices(self): self.assert_stdout('winservices.py') def test_cpu_distribution(self): self.assert_syntax('cpu_distribution.py') - @unittest.skipIf(not HAS_SENSORS_TEMPERATURES, "not supported") + @pytest.mark.skipif(not HAS_SENSORS_TEMPERATURES, reason="not supported") def test_temperatures(self): if not psutil.sensors_temperatures(): - raise unittest.SkipTest("no temperatures") + raise pytest.skip("no temperatures") self.assert_stdout('temperatures.py') - @unittest.skipIf(not HAS_SENSORS_FANS, "not supported") + @pytest.mark.skipif(not HAS_SENSORS_FANS, reason="not supported") def test_fans(self): if not psutil.sensors_fans(): - raise unittest.SkipTest("no fans") + raise pytest.skip("no fans") self.assert_stdout('fans.py') - @unittest.skipIf(not HAS_SENSORS_BATTERY, "not supported") - @unittest.skipIf(not HAS_BATTERY, "no battery") + @pytest.mark.skipif(not HAS_SENSORS_BATTERY, reason="not supported") + @pytest.mark.skipif(not HAS_BATTERY, reason="no battery") def test_battery(self): self.assert_stdout('battery.py') - @unittest.skipIf(not HAS_SENSORS_BATTERY, "not supported") - @unittest.skipIf(not HAS_BATTERY, "no battery") + @pytest.mark.skipif(not HAS_SENSORS_BATTERY, reason="not supported") + @pytest.mark.skipif(not HAS_BATTERY, reason="no battery") def test_sensors(self): self.assert_stdout('sensors.py') - - -if __name__ == '__main__': - from psutil.tests.runner import run_from_name - - run_from_name(__file__) diff --git a/psutil/tests/test_osx.py b/psutil/tests/test_osx.py index 1fe02d3e4..a70cdf641 100755 --- a/psutil/tests/test_osx.py +++ b/psutil/tests/test_osx.py @@ -9,7 +9,6 @@ import platform import re import time -import unittest import psutil from psutil import MACOS @@ -18,6 +17,7 @@ from psutil.tests import TOLERANCE_DISK_USAGE from psutil.tests import TOLERANCE_SYS_MEM from psutil.tests import PsutilTestCase +from psutil.tests import pytest from psutil.tests import retry_on_failure from psutil.tests import sh from psutil.tests import spawn_testproc @@ -51,7 +51,7 @@ def vm_stat(field): return int(re.search(r'\d+', line).group(0)) * getpagesize() -@unittest.skipIf(not MACOS, "MACOS only") +@pytest.mark.skipif(not MACOS, reason="MACOS only") class TestProcess(PsutilTestCase): @classmethod def setUpClass(cls): @@ -67,15 +67,13 @@ def test_process_create_time(self): hhmmss = start_ps.split(' ')[-2] year = start_ps.split(' ')[-1] start_psutil = psutil.Process(self.pid).create_time() - self.assertEqual( - hhmmss, time.strftime("%H:%M:%S", time.localtime(start_psutil)) - ) - self.assertEqual( - year, time.strftime("%Y", time.localtime(start_psutil)) + assert hhmmss == time.strftime( + "%H:%M:%S", time.localtime(start_psutil) ) + assert year == time.strftime("%Y", time.localtime(start_psutil)) -@unittest.skipIf(not MACOS, "MACOS only") +@pytest.mark.skipif(not MACOS, reason="MACOS only") class TestSystemAPIs(PsutilTestCase): # --- disk @@ -100,70 +98,60 @@ def df(path): for part in psutil.disk_partitions(all=False): usage = psutil.disk_usage(part.mountpoint) dev, total, used, free = df(part.mountpoint) - self.assertEqual(part.device, dev) - self.assertEqual(usage.total, total) - self.assertAlmostEqual( - usage.free, free, delta=TOLERANCE_DISK_USAGE - ) - self.assertAlmostEqual( - usage.used, used, delta=TOLERANCE_DISK_USAGE - ) + assert part.device == dev + assert usage.total == total + assert abs(usage.free - free) < TOLERANCE_DISK_USAGE + assert abs(usage.used - used) < TOLERANCE_DISK_USAGE # --- cpu def test_cpu_count_logical(self): num = sysctl("sysctl hw.logicalcpu") - self.assertEqual(num, psutil.cpu_count(logical=True)) + assert num == psutil.cpu_count(logical=True) def test_cpu_count_cores(self): num = sysctl("sysctl hw.physicalcpu") - self.assertEqual(num, psutil.cpu_count(logical=False)) + assert num == psutil.cpu_count(logical=False) # TODO: remove this once 1892 is fixed - @unittest.skipIf( - MACOS and platform.machine() == 'arm64', "skipped due to #1892" + @pytest.mark.skipif( + MACOS and platform.machine() == 'arm64', reason="skipped due to #1892" ) def test_cpu_freq(self): freq = psutil.cpu_freq() - self.assertEqual( - freq.current * 1000 * 1000, sysctl("sysctl hw.cpufrequency") - ) - self.assertEqual( - freq.min * 1000 * 1000, sysctl("sysctl hw.cpufrequency_min") - ) - self.assertEqual( - freq.max * 1000 * 1000, sysctl("sysctl hw.cpufrequency_max") - ) + assert freq.current * 1000 * 1000 == sysctl("sysctl hw.cpufrequency") + assert freq.min * 1000 * 1000 == sysctl("sysctl hw.cpufrequency_min") + assert freq.max * 1000 * 1000 == sysctl("sysctl hw.cpufrequency_max") # --- virtual mem def test_vmem_total(self): sysctl_hwphymem = sysctl('sysctl hw.memsize') - self.assertEqual(sysctl_hwphymem, psutil.virtual_memory().total) + assert sysctl_hwphymem == psutil.virtual_memory().total @retry_on_failure() def test_vmem_free(self): vmstat_val = vm_stat("free") psutil_val = psutil.virtual_memory().free - self.assertAlmostEqual(psutil_val, vmstat_val, delta=TOLERANCE_SYS_MEM) + assert abs(psutil_val - vmstat_val) < TOLERANCE_SYS_MEM @retry_on_failure() def test_vmem_active(self): vmstat_val = vm_stat("active") psutil_val = psutil.virtual_memory().active - self.assertAlmostEqual(psutil_val, vmstat_val, delta=TOLERANCE_SYS_MEM) + assert abs(psutil_val - vmstat_val) < TOLERANCE_SYS_MEM @retry_on_failure() def test_vmem_inactive(self): vmstat_val = vm_stat("inactive") psutil_val = psutil.virtual_memory().inactive - self.assertAlmostEqual(psutil_val, vmstat_val, delta=TOLERANCE_SYS_MEM) + assert abs(psutil_val - vmstat_val) < TOLERANCE_SYS_MEM @retry_on_failure() def test_vmem_wired(self): vmstat_val = vm_stat("wired") psutil_val = psutil.virtual_memory().wired - self.assertAlmostEqual(psutil_val, vmstat_val, delta=TOLERANCE_SYS_MEM) + assert abs(psutil_val - vmstat_val) < TOLERANCE_SYS_MEM # --- swap mem @@ -171,13 +159,13 @@ def test_vmem_wired(self): def test_swapmem_sin(self): vmstat_val = vm_stat("Pageins") psutil_val = psutil.swap_memory().sin - self.assertAlmostEqual(psutil_val, vmstat_val, delta=TOLERANCE_SYS_MEM) + assert abs(psutil_val - vmstat_val) < TOLERANCE_SYS_MEM @retry_on_failure() def test_swapmem_sout(self): vmstat_val = vm_stat("Pageout") psutil_val = psutil.swap_memory().sout - self.assertAlmostEqual(psutil_val, vmstat_val, delta=TOLERANCE_SYS_MEM) + assert abs(psutil_val - vmstat_val) < TOLERANCE_SYS_MEM # --- network @@ -188,25 +176,17 @@ def test_net_if_stats(self): except RuntimeError: pass else: - self.assertEqual(stats.isup, 'RUNNING' in out, msg=out) - self.assertEqual( - stats.mtu, int(re.findall(r'mtu (\d+)', out)[0]) - ) + assert stats.isup == ('RUNNING' in out), out + assert stats.mtu == int(re.findall(r'mtu (\d+)', out)[0]) # --- sensors_battery - @unittest.skipIf(not HAS_BATTERY, "no battery") + @pytest.mark.skipif(not HAS_BATTERY, reason="no battery") def test_sensors_battery(self): out = sh("pmset -g batt") percent = re.search(r"(\d+)%", out).group(1) drawing_from = re.search("Now drawing from '([^']+)'", out).group(1) power_plugged = drawing_from == "AC Power" psutil_result = psutil.sensors_battery() - self.assertEqual(psutil_result.power_plugged, power_plugged) - self.assertEqual(psutil_result.percent, int(percent)) - - -if __name__ == '__main__': - from psutil.tests.runner import run_from_name - - run_from_name(__file__) + assert psutil_result.power_plugged == power_plugged + assert psutil_result.percent == int(percent) diff --git a/psutil/tests/test_posix.py b/psutil/tests/test_posix.py index 941f0fac1..6f7f9790f 100755 --- a/psutil/tests/test_posix.py +++ b/psutil/tests/test_posix.py @@ -13,7 +13,6 @@ import re import subprocess import time -import unittest import psutil from psutil import AIX @@ -23,11 +22,13 @@ from psutil import OPENBSD from psutil import POSIX from psutil import SUNOS +from psutil.tests import AARCH64 from psutil.tests import HAS_NET_IO_COUNTERS from psutil.tests import PYTHON_EXE from psutil.tests import 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 @@ -134,7 +135,23 @@ def ps_vsz(pid): return ps(field, pid) -@unittest.skipIf(not POSIX, "POSIX only") +def df(device): + try: + out = sh("df -k %s" % device).strip() + except RuntimeError as err: + if "device busy" in str(err).lower(): + raise pytest.skip("df returned EBUSY") + raise + line = out.split('\n')[1] + fields = line.split() + sys_total = int(fields[1]) * 1024 + sys_used = int(fields[2]) * 1024 + sys_free = int(fields[3]) * 1024 + sys_percent = float(fields[4].replace('%', '')) + return (sys_total, sys_used, sys_free, sys_percent) + + +@pytest.mark.skipif(not POSIX, reason="POSIX only") class TestProcess(PsutilTestCase): """Compare psutil results against 'ps' command line utility (mainly).""" @@ -151,22 +168,22 @@ def tearDownClass(cls): def test_ppid(self): ppid_ps = ps('ppid', self.pid) ppid_psutil = psutil.Process(self.pid).ppid() - self.assertEqual(ppid_ps, ppid_psutil) + assert ppid_ps == ppid_psutil def test_uid(self): uid_ps = ps('uid', self.pid) uid_psutil = psutil.Process(self.pid).uids().real - self.assertEqual(uid_ps, uid_psutil) + assert uid_ps == uid_psutil def test_gid(self): gid_ps = ps('rgid', self.pid) gid_psutil = psutil.Process(self.pid).gids().real - self.assertEqual(gid_ps, gid_psutil) + assert gid_ps == gid_psutil def test_username(self): username_ps = ps('user', self.pid) username_psutil = psutil.Process(self.pid).username() - self.assertEqual(username_ps, username_psutil) + assert username_ps == username_psutil def test_username_no_resolution(self): # Emulate a case where the system can't resolve the uid to @@ -174,7 +191,7 @@ def test_username_no_resolution(self): # the stringified uid. p = psutil.Process() with mock.patch("psutil.pwd.getpwuid", side_effect=KeyError) as fun: - self.assertEqual(p.username(), str(p.uids().real)) + assert p.username() == str(p.uids().real) assert fun.called @skip_on_access_denied() @@ -185,7 +202,7 @@ def test_rss_memory(self): time.sleep(0.1) rss_ps = ps_rss(self.pid) rss_psutil = psutil.Process(self.pid).memory_info()[0] / 1024 - self.assertEqual(rss_ps, rss_psutil) + assert rss_ps == rss_psutil @skip_on_access_denied() @retry_on_failure() @@ -195,7 +212,7 @@ def test_vsz_memory(self): time.sleep(0.1) vsz_ps = ps_vsz(self.pid) vsz_psutil = psutil.Process(self.pid).memory_info()[1] / 1024 - self.assertEqual(vsz_ps, vsz_psutil) + assert vsz_ps == vsz_psutil def test_name(self): name_ps = ps_name(self.pid) @@ -209,7 +226,7 @@ def test_name(self): # ...may also be "python.X" name_ps = re.sub(r"\d", "", name_ps) name_psutil = re.sub(r"\d", "", name_psutil) - self.assertEqual(name_ps, name_psutil) + assert name_ps == name_psutil def test_name_long(self): # On UNIX the kernel truncates the name to the first 15 @@ -222,7 +239,7 @@ def test_name_long(self): "psutil._psplatform.Process.cmdline", return_value=cmdline ): p = psutil.Process() - self.assertEqual(p.name(), "long-program-name-extended") + assert p.name() == "long-program-name-extended" def test_name_long_cmdline_ad_exc(self): # Same as above but emulates a case where cmdline() raises @@ -235,7 +252,7 @@ def test_name_long_cmdline_ad_exc(self): side_effect=psutil.AccessDenied(0, ""), ): p = psutil.Process() - self.assertEqual(p.name(), "long-program-name") + assert p.name() == "long-program-name" def test_name_long_cmdline_nsp_exc(self): # Same as above but emulates a case where cmdline() raises NSP @@ -247,9 +264,10 @@ def test_name_long_cmdline_nsp_exc(self): side_effect=psutil.NoSuchProcess(0, ""), ): p = psutil.Process() - self.assertRaises(psutil.NoSuchProcess, p.name) + with pytest.raises(psutil.NoSuchProcess): + p.name() - @unittest.skipIf(MACOS or BSD, 'ps -o start not available') + @pytest.mark.skipif(MACOS or BSD, reason="ps -o start not available") def test_create_time(self): time_ps = ps('start', self.pid) time_psutil = psutil.Process(self.pid).create_time() @@ -262,13 +280,13 @@ def test_create_time(self): round_time_psutil_tstamp = datetime.datetime.fromtimestamp( round_time_psutil ).strftime("%H:%M:%S") - self.assertIn(time_ps, [time_psutil_tstamp, round_time_psutil_tstamp]) + assert time_ps in [time_psutil_tstamp, round_time_psutil_tstamp] def test_exe(self): ps_pathname = ps_name(self.pid) psutil_pathname = psutil.Process(self.pid).exe() try: - self.assertEqual(ps_pathname, psutil_pathname) + assert ps_pathname == psutil_pathname except AssertionError: # certain platforms such as BSD are more accurate returning: # "/usr/local/bin/python2.7" @@ -277,7 +295,7 @@ def test_exe(self): # We do not want to consider this difference in accuracy # an error. adjusted_ps_pathname = ps_pathname[: len(ps_pathname)] - self.assertEqual(ps_pathname, adjusted_ps_pathname) + assert ps_pathname == adjusted_ps_pathname # On macOS the official python installer exposes a python wrapper that # executes a python executable hidden inside an application bundle inside @@ -288,22 +306,25 @@ def test_exe(self): def test_cmdline(self): ps_cmdline = ps_args(self.pid) psutil_cmdline = " ".join(psutil.Process(self.pid).cmdline()) - self.assertEqual(ps_cmdline, psutil_cmdline) + if AARCH64 and len(ps_cmdline) < len(psutil_cmdline): + assert psutil_cmdline.startswith(ps_cmdline) + else: + assert ps_cmdline == psutil_cmdline # On SUNOS "ps" reads niceness /proc/pid/psinfo which returns an # incorrect value (20); the real deal is getpriority(2) which # returns 0; psutil relies on it, see: # https://github.com/giampaolo/psutil/issues/1082 # AIX has the same issue - @unittest.skipIf(SUNOS, "not reliable on SUNOS") - @unittest.skipIf(AIX, "not reliable on AIX") + @pytest.mark.skipif(SUNOS, reason="not reliable on SUNOS") + @pytest.mark.skipif(AIX, reason="not reliable on AIX") def test_nice(self): ps_nice = ps('nice', self.pid) psutil_nice = psutil.Process().nice() - self.assertEqual(ps_nice, psutil_nice) + assert ps_nice == psutil_nice -@unittest.skipIf(not POSIX, "POSIX only") +@pytest.mark.skipif(not POSIX, reason="POSIX only") class TestSystemAPIs(PsutilTestCase): """Test some system APIs.""" @@ -315,7 +336,7 @@ def test_pids(self): pids_psutil = psutil.pids() # on MACOS and OPENBSD ps doesn't show pid 0 - if MACOS or OPENBSD and 0 not in pids_ps: + if MACOS or (OPENBSD and 0 not in pids_ps): pids_ps.insert(0, 0) # There will often be one more process in pids_ps for ps itself @@ -327,9 +348,9 @@ def test_pids(self): # for some reason ifconfig -a does not report all interfaces # returned by psutil - @unittest.skipIf(SUNOS, "unreliable on SUNOS") - @unittest.skipIf(not which('ifconfig'), "no ifconfig cmd") - @unittest.skipIf(not HAS_NET_IO_COUNTERS, "not supported") + @pytest.mark.skipif(SUNOS, reason="unreliable on SUNOS") + @pytest.mark.skipif(not which('ifconfig'), reason="no ifconfig cmd") + @pytest.mark.skipif(not HAS_NET_IO_COUNTERS, reason="not supported") def test_nic_names(self): output = sh("ifconfig -a") for nic in psutil.net_io_counters(pernic=True): @@ -342,20 +363,21 @@ def test_nic_names(self): % (nic, output) ) - # @unittest.skipIf(CI_TESTING and not psutil.users(), "unreliable on CI") + # @pytest.mark.skipif(CI_TESTING and not psutil.users(), + # reason="unreliable on CI") @retry_on_failure() def test_users(self): out = sh("who -u") if not out.strip(): - raise unittest.SkipTest("no users on this system") + raise pytest.skip("no users on this system") lines = out.split('\n') users = [x.split()[0] for x in lines] terminals = [x.split()[1] for x in lines] - self.assertEqual(len(users), len(psutil.users())) + assert len(users) == len(psutil.users()) with self.subTest(psutil=psutil.users(), who=out): for idx, u in enumerate(psutil.users()): - self.assertEqual(u.name, users[idx]) - self.assertEqual(u.terminal, terminals[idx]) + assert u.name == users[idx] + assert u.terminal == terminals[idx] if u.pid is not None: # None on OpenBSD psutil.Process(u.pid) @@ -363,7 +385,7 @@ def test_users(self): def test_users_started(self): out = sh("who -u") if not out.strip(): - raise unittest.SkipTest("no users on this system") + raise pytest.skip("no users on this system") tstamp = None # '2023-04-11 09:31' (Linux) started = re.findall(r"\d\d\d\d-\d\d-\d\d \d\d:\d\d", out) @@ -387,7 +409,7 @@ def test_users_started(self): started = [x.capitalize() for x in started] if not tstamp: - raise unittest.SkipTest( + raise pytest.skip( "cannot interpret tstamp in who output\n%s" % (out) ) @@ -396,7 +418,7 @@ def test_users_started(self): psutil_value = datetime.datetime.fromtimestamp( u.started ).strftime(tstamp) - self.assertEqual(psutil_value, started[idx]) + assert psutil_value == started[idx] def test_pid_exists_let_raise(self): # According to "man 2 kill" possible error values for kill @@ -405,7 +427,8 @@ def test_pid_exists_let_raise(self): with mock.patch( "psutil._psposix.os.kill", side_effect=OSError(errno.EBADF, "") ) as m: - self.assertRaises(OSError, psutil._psposix.pid_exists, os.getpid()) + with pytest.raises(OSError): + psutil._psposix.pid_exists(os.getpid()) assert m.called def test_os_waitpid_let_raise(self): @@ -414,7 +437,8 @@ def test_os_waitpid_let_raise(self): with mock.patch( "psutil._psposix.os.waitpid", side_effect=OSError(errno.EBADF, "") ) as m: - self.assertRaises(OSError, psutil._psposix.wait_pid, os.getpid()) + with pytest.raises(OSError): + psutil._psposix.wait_pid(os.getpid()) assert m.called def test_os_waitpid_eintr(self): @@ -422,12 +446,8 @@ def test_os_waitpid_eintr(self): with mock.patch( "psutil._psposix.os.waitpid", side_effect=OSError(errno.EINTR, "") ) as m: - self.assertRaises( - psutil._psposix.TimeoutExpired, - psutil._psposix.wait_pid, - os.getpid(), - timeout=0.01, - ) + with pytest.raises(psutil._psposix.TimeoutExpired): + psutil._psposix.wait_pid(os.getpid(), timeout=0.01) assert m.called def test_os_waitpid_bad_ret_status(self): @@ -435,35 +455,19 @@ def test_os_waitpid_bad_ret_status(self): with mock.patch( "psutil._psposix.os.waitpid", return_value=(1, -1) ) as m: - self.assertRaises( - ValueError, psutil._psposix.wait_pid, os.getpid() - ) + with pytest.raises(ValueError): + psutil._psposix.wait_pid(os.getpid()) assert m.called # AIX can return '-' in df output instead of numbers, e.g. for /proc - @unittest.skipIf(AIX, "unreliable on AIX") + @pytest.mark.skipif(AIX, reason="unreliable on AIX") @retry_on_failure() def test_disk_usage(self): - def df(device): - try: - out = sh("df -k %s" % device).strip() - except RuntimeError as err: - if "device busy" in str(err).lower(): - raise unittest.SkipTest("df returned EBUSY") - raise - line = out.split('\n')[1] - fields = line.split() - total = int(fields[1]) * 1024 - used = int(fields[2]) * 1024 - free = int(fields[3]) * 1024 - percent = float(fields[4].replace('%', '')) - return (total, used, free, percent) - tolerance = 4 * 1024 * 1024 # 4MB for part in psutil.disk_partitions(all=False): usage = psutil.disk_usage(part.mountpoint) try: - total, used, free, percent = df(part.device) + sys_total, sys_used, sys_free, sys_percent = df(part.device) except RuntimeError as err: # see: # https://travis-ci.org/giampaolo/psutil/jobs/138338464 @@ -477,22 +481,16 @@ def df(device): continue raise else: - self.assertAlmostEqual(usage.total, total, delta=tolerance) - self.assertAlmostEqual(usage.used, used, delta=tolerance) - self.assertAlmostEqual(usage.free, free, delta=tolerance) - self.assertAlmostEqual(usage.percent, percent, delta=1) + assert abs(usage.total - sys_total) < tolerance + assert abs(usage.used - sys_used) < tolerance + assert abs(usage.free - sys_free) < tolerance + assert abs(usage.percent - sys_percent) <= 1 -@unittest.skipIf(not POSIX, "POSIX only") +@pytest.mark.skipif(not POSIX, reason="POSIX only") class TestMisc(PsutilTestCase): def test_getpagesize(self): pagesize = getpagesize() - self.assertGreater(pagesize, 0) - self.assertEqual(pagesize, resource.getpagesize()) - self.assertEqual(pagesize, mmap.PAGESIZE) - - -if __name__ == '__main__': - from psutil.tests.runner import run_from_name - - run_from_name(__file__) + assert pagesize > 0 + assert pagesize == resource.getpagesize() + assert pagesize == mmap.PAGESIZE diff --git a/psutil/tests/test_process.py b/psutil/tests/test_process.py index 363474c78..b8f06a46e 100755 --- a/psutil/tests/test_process.py +++ b/psutil/tests/test_process.py @@ -19,7 +19,6 @@ import textwrap import time import types -import unittest import psutil from psutil import AIX @@ -63,6 +62,7 @@ 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 from psutil.tests import retry_on_failure from psutil.tests import sh @@ -91,8 +91,8 @@ def spawn_psproc(self, *args, **kwargs): def test_pid(self): p = psutil.Process() - self.assertEqual(p.pid, os.getpid()) - with self.assertRaises(AttributeError): + assert p.pid == os.getpid() + with pytest.raises(AttributeError): p.pid = 33 def test_kill(self): @@ -100,9 +100,9 @@ def test_kill(self): p.kill() code = p.wait() if WINDOWS: - self.assertEqual(code, signal.SIGTERM) + assert code == signal.SIGTERM else: - self.assertEqual(code, -signal.SIGKILL) + assert code == -signal.SIGKILL self.assertProcessGone(p) def test_terminate(self): @@ -110,9 +110,9 @@ def test_terminate(self): p.terminate() code = p.wait() if WINDOWS: - self.assertEqual(code, signal.SIGTERM) + assert code == signal.SIGTERM else: - self.assertEqual(code, -signal.SIGTERM) + assert code == -signal.SIGTERM self.assertProcessGone(p) def test_send_signal(self): @@ -121,25 +121,27 @@ def test_send_signal(self): p.send_signal(sig) code = p.wait() if WINDOWS: - self.assertEqual(code, sig) + assert code == sig else: - self.assertEqual(code, -sig) + assert code == -sig self.assertProcessGone(p) - @unittest.skipIf(not POSIX, "not POSIX") + @pytest.mark.skipif(not POSIX, reason="not POSIX") def test_send_signal_mocked(self): sig = signal.SIGTERM p = self.spawn_psproc() with mock.patch( 'psutil.os.kill', side_effect=OSError(errno.ESRCH, "") ): - self.assertRaises(psutil.NoSuchProcess, p.send_signal, sig) + with pytest.raises(psutil.NoSuchProcess): + p.send_signal(sig) p = self.spawn_psproc() with mock.patch( 'psutil.os.kill', side_effect=OSError(errno.EPERM, "") ): - self.assertRaises(psutil.AccessDenied, p.send_signal, sig) + with pytest.raises(psutil.AccessDenied): + p.send_signal(sig) def test_wait_exited(self): # Test waitpid() + WIFEXITED -> WEXITSTATUS. @@ -147,55 +149,61 @@ def test_wait_exited(self): cmd = [PYTHON_EXE, "-c", "pass"] p = self.spawn_psproc(cmd) code = p.wait() - self.assertEqual(code, 0) + assert code == 0 self.assertProcessGone(p) # exit(1), implicit in case of error cmd = [PYTHON_EXE, "-c", "1 / 0"] p = self.spawn_psproc(cmd, stderr=subprocess.PIPE) code = p.wait() - self.assertEqual(code, 1) + assert code == 1 self.assertProcessGone(p) # via sys.exit() cmd = [PYTHON_EXE, "-c", "import sys; sys.exit(5);"] p = self.spawn_psproc(cmd) code = p.wait() - self.assertEqual(code, 5) + assert code == 5 self.assertProcessGone(p) # via os._exit() cmd = [PYTHON_EXE, "-c", "import os; os._exit(5);"] p = self.spawn_psproc(cmd) code = p.wait() - self.assertEqual(code, 5) + assert code == 5 self.assertProcessGone(p) - @unittest.skipIf(NETBSD, "fails on NETBSD") + @pytest.mark.skipif(NETBSD, reason="fails on NETBSD") def test_wait_stopped(self): p = self.spawn_psproc() if POSIX: # Test waitpid() + WIFSTOPPED and WIFCONTINUED. # Note: if a process is stopped it ignores SIGTERM. p.send_signal(signal.SIGSTOP) - self.assertRaises(psutil.TimeoutExpired, p.wait, timeout=0.001) + with pytest.raises(psutil.TimeoutExpired): + p.wait(timeout=0.001) p.send_signal(signal.SIGCONT) - self.assertRaises(psutil.TimeoutExpired, p.wait, timeout=0.001) + with pytest.raises(psutil.TimeoutExpired): + p.wait(timeout=0.001) p.send_signal(signal.SIGTERM) - self.assertEqual(p.wait(), -signal.SIGTERM) - self.assertEqual(p.wait(), -signal.SIGTERM) + assert p.wait() == -signal.SIGTERM + assert p.wait() == -signal.SIGTERM else: p.suspend() - self.assertRaises(psutil.TimeoutExpired, p.wait, timeout=0.001) + with pytest.raises(psutil.TimeoutExpired): + p.wait(timeout=0.001) p.resume() - self.assertRaises(psutil.TimeoutExpired, p.wait, timeout=0.001) + with pytest.raises(psutil.TimeoutExpired): + p.wait(timeout=0.001) p.terminate() - self.assertEqual(p.wait(), signal.SIGTERM) - self.assertEqual(p.wait(), signal.SIGTERM) + assert p.wait() == signal.SIGTERM + assert p.wait() == signal.SIGTERM def test_wait_non_children(self): # Test wait() against a process which is not our direct # child. child, grandchild = self.spawn_children_pair() - self.assertRaises(psutil.TimeoutExpired, child.wait, 0.01) - self.assertRaises(psutil.TimeoutExpired, grandchild.wait, 0.01) + with pytest.raises(psutil.TimeoutExpired): + child.wait(0.01) + with pytest.raises(psutil.TimeoutExpired): + grandchild.wait(0.01) # We also terminate the direct child otherwise the # grandchild will hang until the parent is gone. child.terminate() @@ -203,24 +211,28 @@ def test_wait_non_children(self): child_ret = child.wait() grandchild_ret = grandchild.wait() if POSIX: - self.assertEqual(child_ret, -signal.SIGTERM) + assert child_ret == -signal.SIGTERM # For processes which are not our children we're supposed # to get None. - self.assertEqual(grandchild_ret, None) + assert grandchild_ret is None else: - self.assertEqual(child_ret, signal.SIGTERM) - self.assertEqual(child_ret, signal.SIGTERM) + assert child_ret == signal.SIGTERM + assert child_ret == signal.SIGTERM def test_wait_timeout(self): p = self.spawn_psproc() p.name() - self.assertRaises(psutil.TimeoutExpired, p.wait, 0.01) - self.assertRaises(psutil.TimeoutExpired, p.wait, 0) - self.assertRaises(ValueError, p.wait, -1) + with pytest.raises(psutil.TimeoutExpired): + p.wait(0.01) + with pytest.raises(psutil.TimeoutExpired): + p.wait(0) + with pytest.raises(ValueError): + p.wait(-1) def test_wait_timeout_nonblocking(self): p = self.spawn_psproc() - self.assertRaises(psutil.TimeoutExpired, p.wait, 0) + with pytest.raises(psutil.TimeoutExpired): + p.wait(0) p.kill() stop_at = time.time() + GLOBAL_TIMEOUT while time.time() < stop_at: @@ -232,9 +244,9 @@ def test_wait_timeout_nonblocking(self): else: raise self.fail('timeout') if POSIX: - self.assertEqual(code, -signal.SIGKILL) + assert code == -signal.SIGKILL else: - self.assertEqual(code, signal.SIGTERM) + assert code == signal.SIGTERM self.assertProcessGone(p) def test_cpu_percent(self): @@ -243,9 +255,9 @@ def test_cpu_percent(self): p.cpu_percent(interval=0.001) for _ in range(100): percent = p.cpu_percent(interval=None) - self.assertIsInstance(percent, float) - self.assertGreaterEqual(percent, 0.0) - with self.assertRaises(ValueError): + assert isinstance(percent, float) + assert percent >= 0.0 + with pytest.raises(ValueError): p.cpu_percent(interval=-1) def test_cpu_percent_numcpus_none(self): @@ -254,7 +266,7 @@ def test_cpu_percent_numcpus_none(self): psutil.Process().cpu_percent() assert m.called - @unittest.skipIf(QEMU_USER, "QEMU user not supported") + @pytest.mark.skipif(QEMU_USER, reason="QEMU user not supported") def test_cpu_times(self): times = psutil.Process().cpu_times() assert times.user >= 0.0, times @@ -267,7 +279,7 @@ def test_cpu_times(self): for name in times._fields: time.strftime("%H:%M:%S", time.localtime(getattr(times, name))) - @unittest.skipIf(QEMU_USER, "QEMU user not supported") + @pytest.mark.skipif(QEMU_USER, reason="QEMU user not supported") def test_cpu_times_2(self): user_time, kernel_time = psutil.Process().cpu_times()[:2] utime, ktime = os.times()[:2] @@ -281,14 +293,14 @@ def test_cpu_times_2(self): if (max([kernel_time, ktime]) - min([kernel_time, ktime])) > 0.1: raise self.fail("expected: %s, found: %s" % (ktime, kernel_time)) - @unittest.skipIf(not HAS_PROC_CPU_NUM, "not supported") + @pytest.mark.skipif(not HAS_PROC_CPU_NUM, reason="not supported") def test_cpu_num(self): p = psutil.Process() num = p.cpu_num() - self.assertGreaterEqual(num, 0) + assert num >= 0 if psutil.cpu_count() == 1: - self.assertEqual(num, 0) - self.assertIn(p.cpu_num(), range(psutil.cpu_count())) + assert num == 0 + assert p.cpu_num() in range(psutil.cpu_count()) def test_create_time(self): p = self.spawn_psproc() @@ -308,14 +320,19 @@ def test_create_time(self): # make sure returned value can be pretty printed with strftime time.strftime("%Y %m %d %H:%M:%S", time.localtime(p.create_time())) - @unittest.skipIf(not POSIX, 'POSIX only') + @pytest.mark.skipif(not POSIX, reason="POSIX only") def test_terminal(self): terminal = psutil.Process().terminal() if terminal is not None: - tty = os.path.realpath(sh('tty')) - self.assertEqual(terminal, tty) + try: + tty = os.path.realpath(sh('tty')) + except RuntimeError: + # Note: happens if pytest is run without the `-s` opt. + raise pytest.skip("can't rely on `tty` CLI") + else: + assert terminal == tty - @unittest.skipIf(not HAS_PROC_IO_COUNTERS, 'not supported') + @pytest.mark.skipif(not HAS_PROC_IO_COUNTERS, reason="not supported") @skip_on_not_implemented(only_if=LINUX) def test_io_counters(self): p = psutil.Process() @@ -325,14 +342,14 @@ def test_io_counters(self): f.read() io2 = p.io_counters() if not BSD and not AIX: - self.assertGreater(io2.read_count, io1.read_count) - self.assertEqual(io2.write_count, io1.write_count) + assert io2.read_count > io1.read_count + assert io2.write_count == io1.write_count if LINUX: - self.assertGreater(io2.read_chars, io1.read_chars) - self.assertEqual(io2.write_chars, io1.write_chars) + assert io2.read_chars > io1.read_chars + assert io2.write_chars == io1.write_chars else: - self.assertGreaterEqual(io2.read_bytes, io1.read_bytes) - self.assertGreaterEqual(io2.write_bytes, io1.write_bytes) + assert io2.read_bytes >= io1.read_bytes + assert io2.write_bytes >= io1.write_bytes # test writes io1 = p.io_counters() @@ -342,24 +359,24 @@ def test_io_counters(self): else: f.write("x" * 1000000) io2 = p.io_counters() - self.assertGreaterEqual(io2.write_count, io1.write_count) - self.assertGreaterEqual(io2.write_bytes, io1.write_bytes) - self.assertGreaterEqual(io2.read_count, io1.read_count) - self.assertGreaterEqual(io2.read_bytes, io1.read_bytes) + assert io2.write_count >= io1.write_count + assert io2.write_bytes >= io1.write_bytes + assert io2.read_count >= io1.read_count + assert io2.read_bytes >= io1.read_bytes if LINUX: - self.assertGreater(io2.write_chars, io1.write_chars) - self.assertGreaterEqual(io2.read_chars, io1.read_chars) + assert io2.write_chars > io1.write_chars + assert io2.read_chars >= io1.read_chars # sanity check for i in range(len(io2)): if BSD and i >= 2: # On BSD read_bytes and write_bytes are always set to -1. continue - self.assertGreaterEqual(io2[i], 0) - self.assertGreaterEqual(io2[i], 0) + assert io2[i] >= 0 + assert io2[i] >= 0 - @unittest.skipIf(not HAS_IONICE, "not supported") - @unittest.skipIf(not LINUX, "linux only") + @pytest.mark.skipif(not HAS_IONICE, reason="not supported") + @pytest.mark.skipif(not LINUX, reason="linux only") def test_ionice_linux(self): def cleanup(init): ioclass, value = init @@ -369,69 +386,71 @@ def cleanup(init): p = psutil.Process() if not CI_TESTING: - self.assertEqual(p.ionice()[0], psutil.IOPRIO_CLASS_NONE) - self.assertEqual(psutil.IOPRIO_CLASS_NONE, 0) - self.assertEqual(psutil.IOPRIO_CLASS_RT, 1) # high - self.assertEqual(psutil.IOPRIO_CLASS_BE, 2) # normal - self.assertEqual(psutil.IOPRIO_CLASS_IDLE, 3) # low + assert p.ionice()[0] == psutil.IOPRIO_CLASS_NONE + assert psutil.IOPRIO_CLASS_NONE == 0 + assert psutil.IOPRIO_CLASS_RT == 1 # high + assert psutil.IOPRIO_CLASS_BE == 2 # normal + assert psutil.IOPRIO_CLASS_IDLE == 3 # low init = p.ionice() self.addCleanup(cleanup, init) # low p.ionice(psutil.IOPRIO_CLASS_IDLE) - self.assertEqual(tuple(p.ionice()), (psutil.IOPRIO_CLASS_IDLE, 0)) - with self.assertRaises(ValueError): # accepts no value + assert tuple(p.ionice()) == (psutil.IOPRIO_CLASS_IDLE, 0) + with pytest.raises(ValueError): # accepts no value p.ionice(psutil.IOPRIO_CLASS_IDLE, value=7) # normal p.ionice(psutil.IOPRIO_CLASS_BE) - self.assertEqual(tuple(p.ionice()), (psutil.IOPRIO_CLASS_BE, 0)) + assert tuple(p.ionice()) == (psutil.IOPRIO_CLASS_BE, 0) p.ionice(psutil.IOPRIO_CLASS_BE, value=7) - self.assertEqual(tuple(p.ionice()), (psutil.IOPRIO_CLASS_BE, 7)) - with self.assertRaises(ValueError): + assert tuple(p.ionice()) == (psutil.IOPRIO_CLASS_BE, 7) + with pytest.raises(ValueError): p.ionice(psutil.IOPRIO_CLASS_BE, value=8) try: p.ionice(psutil.IOPRIO_CLASS_RT, value=7) except psutil.AccessDenied: pass # errs - with self.assertRaisesRegex(ValueError, "ioclass accepts no value"): + with pytest.raises(ValueError, match="ioclass accepts no value"): p.ionice(psutil.IOPRIO_CLASS_NONE, 1) - with self.assertRaisesRegex(ValueError, "ioclass accepts no value"): + with pytest.raises(ValueError, match="ioclass accepts no value"): p.ionice(psutil.IOPRIO_CLASS_IDLE, 1) - with self.assertRaisesRegex( - ValueError, "'ioclass' argument must be specified" + with pytest.raises( + ValueError, match="'ioclass' argument must be specified" ): p.ionice(value=1) - @unittest.skipIf(not HAS_IONICE, "not supported") - @unittest.skipIf(not WINDOWS, 'not supported on this win version') + @pytest.mark.skipif(not HAS_IONICE, reason="not supported") + @pytest.mark.skipif( + not WINDOWS, reason="not supported on this win version" + ) def test_ionice_win(self): p = psutil.Process() if not CI_TESTING: - self.assertEqual(p.ionice(), psutil.IOPRIO_NORMAL) + assert p.ionice() == psutil.IOPRIO_NORMAL init = p.ionice() self.addCleanup(p.ionice, init) # base p.ionice(psutil.IOPRIO_VERYLOW) - self.assertEqual(p.ionice(), psutil.IOPRIO_VERYLOW) + assert p.ionice() == psutil.IOPRIO_VERYLOW p.ionice(psutil.IOPRIO_LOW) - self.assertEqual(p.ionice(), psutil.IOPRIO_LOW) + assert p.ionice() == psutil.IOPRIO_LOW try: p.ionice(psutil.IOPRIO_HIGH) except psutil.AccessDenied: pass else: - self.assertEqual(p.ionice(), psutil.IOPRIO_HIGH) + assert p.ionice() == psutil.IOPRIO_HIGH # errs - with self.assertRaisesRegex( - TypeError, "value argument not accepted on Windows" + with pytest.raises( + TypeError, match="value argument not accepted on Windows" ): p.ionice(psutil.IOPRIO_NORMAL, value=1) - with self.assertRaisesRegex(ValueError, "is not a valid priority"): + with pytest.raises(ValueError, match="is not a valid priority"): p.ionice(psutil.IOPRIO_HIGH + 1) - @unittest.skipIf(not HAS_RLIMIT, "not supported") + @pytest.mark.skipif(not HAS_RLIMIT, reason="not supported") def test_rlimit_get(self): import resource @@ -440,35 +459,35 @@ def test_rlimit_get(self): assert names, names for name in names: value = getattr(psutil, name) - self.assertGreaterEqual(value, 0) + assert value >= 0 if name in dir(resource): - self.assertEqual(value, getattr(resource, name)) + assert value == getattr(resource, name) # XXX - On PyPy RLIMIT_INFINITY returned by # resource.getrlimit() is reported as a very big long # number instead of -1. It looks like a bug with PyPy. if PYPY: continue - self.assertEqual(p.rlimit(value), resource.getrlimit(value)) + assert p.rlimit(value) == resource.getrlimit(value) else: ret = p.rlimit(value) - self.assertEqual(len(ret), 2) - self.assertGreaterEqual(ret[0], -1) - self.assertGreaterEqual(ret[1], -1) + assert len(ret) == 2 + assert ret[0] >= -1 + assert ret[1] >= -1 - @unittest.skipIf(not HAS_RLIMIT, "not supported") + @pytest.mark.skipif(not HAS_RLIMIT, reason="not supported") def test_rlimit_set(self): p = self.spawn_psproc() p.rlimit(psutil.RLIMIT_NOFILE, (5, 5)) - self.assertEqual(p.rlimit(psutil.RLIMIT_NOFILE), (5, 5)) + assert p.rlimit(psutil.RLIMIT_NOFILE) == (5, 5) # If pid is 0 prlimit() applies to the calling process and # we don't want that. if LINUX: - with self.assertRaisesRegex(ValueError, "can't use prlimit"): + with pytest.raises(ValueError, match="can't use prlimit"): psutil._psplatform.Process(0).rlimit(0) - with self.assertRaises(ValueError): + with pytest.raises(ValueError): p.rlimit(psutil.RLIMIT_NOFILE, (5, 5, 5)) - @unittest.skipIf(not HAS_RLIMIT, "not supported") + @pytest.mark.skipif(not HAS_RLIMIT, reason="not supported") def test_rlimit(self): p = psutil.Process() testfn = self.get_testfn() @@ -479,17 +498,15 @@ def test_rlimit(self): f.write(b"X" * 1024) # write() or flush() doesn't always cause the exception # but close() will. - with self.assertRaises(IOError) as exc: + with pytest.raises(IOError) as exc: with open(testfn, "wb") as f: f.write(b"X" * 1025) - self.assertEqual( - exc.exception.errno if PY3 else exc.exception[0], errno.EFBIG - ) + assert (exc.value.errno if PY3 else exc.value[0]) == errno.EFBIG finally: p.rlimit(psutil.RLIMIT_FSIZE, (soft, hard)) - self.assertEqual(p.rlimit(psutil.RLIMIT_FSIZE), (soft, hard)) + assert p.rlimit(psutil.RLIMIT_FSIZE) == (soft, hard) - @unittest.skipIf(not HAS_RLIMIT, "not supported") + @pytest.mark.skipif(not HAS_RLIMIT, reason="not supported") def test_rlimit_infinity(self): # First set a limit, then re-set it by specifying INFINITY # and assume we overridden the previous limit. @@ -502,9 +519,9 @@ def test_rlimit_infinity(self): f.write(b"X" * 2048) finally: p.rlimit(psutil.RLIMIT_FSIZE, (soft, hard)) - self.assertEqual(p.rlimit(psutil.RLIMIT_FSIZE), (soft, hard)) + assert p.rlimit(psutil.RLIMIT_FSIZE) == (soft, hard) - @unittest.skipIf(not HAS_RLIMIT, "not supported") + @pytest.mark.skipif(not HAS_RLIMIT, reason="not supported") def test_rlimit_infinity_value(self): # RLIMIT_FSIZE should be RLIM_INFINITY, which will be a really # big number on a platform with large file support. On these @@ -513,7 +530,7 @@ def test_rlimit_infinity_value(self): # conversion doesn't raise an error. p = psutil.Process() soft, hard = p.rlimit(psutil.RLIMIT_FSIZE) - self.assertEqual(psutil.RLIM_INFINITY, hard) + assert hard == psutil.RLIM_INFINITY p.rlimit(psutil.RLIMIT_FSIZE, (soft, hard)) def test_num_threads(self): @@ -525,59 +542,60 @@ def test_num_threads(self): try: step1 = p.num_threads() except psutil.AccessDenied: - raise unittest.SkipTest("on OpenBSD this requires root access") + raise pytest.skip("on OpenBSD this requires root access") else: step1 = p.num_threads() with ThreadTask(): step2 = p.num_threads() - self.assertEqual(step2, step1 + 1) + assert step2 == step1 + 1 - @unittest.skipIf(not WINDOWS, 'WINDOWS only') + @pytest.mark.skipif(not WINDOWS, reason="WINDOWS only") def test_num_handles(self): # a better test is done later into test/_windows.py p = psutil.Process() - self.assertGreater(p.num_handles(), 0) + assert p.num_handles() > 0 - @unittest.skipIf(not HAS_THREADS, 'not supported') + @pytest.mark.skipif(not HAS_THREADS, reason="not supported") def test_threads(self): p = psutil.Process() if OPENBSD: try: step1 = p.threads() except psutil.AccessDenied: - raise unittest.SkipTest("on OpenBSD this requires root access") + raise pytest.skip("on OpenBSD this requires root access") else: step1 = p.threads() with ThreadTask(): step2 = p.threads() - self.assertEqual(len(step2), len(step1) + 1) + assert len(step2) == len(step1) + 1 athread = step2[0] # test named tuple - self.assertEqual(athread.id, athread[0]) - self.assertEqual(athread.user_time, athread[1]) - self.assertEqual(athread.system_time, athread[2]) + assert athread.id == athread[0] + assert athread.user_time == athread[1] + assert athread.system_time == athread[2] @retry_on_failure() @skip_on_access_denied(only_if=MACOS) - @unittest.skipIf(not HAS_THREADS, 'not supported') + @pytest.mark.skipif(not HAS_THREADS, reason="not supported") def test_threads_2(self): p = self.spawn_psproc() if OPENBSD: try: p.threads() except psutil.AccessDenied: - raise unittest.SkipTest("on OpenBSD this requires root access") - self.assertAlmostEqual( - p.cpu_times().user, - sum([x.user_time for x in p.threads()]), - delta=0.1, + raise pytest.skip("on OpenBSD this requires root access") + assert ( + abs(p.cpu_times().user - sum([x.user_time for x in p.threads()])) + < 0.1 ) - self.assertAlmostEqual( - p.cpu_times().system, - sum([x.system_time for x in p.threads()]), - delta=0.1, + assert ( + abs( + p.cpu_times().system + - sum([x.system_time for x in p.threads()]) + ) + < 0.1 ) @retry_on_failure() @@ -587,8 +605,8 @@ def test_memory_info(self): # step 1 - get a base value to compare our results rss1, vms1 = p.memory_info()[:2] percent1 = p.memory_percent() - self.assertGreater(rss1, 0) - self.assertGreater(vms1, 0) + assert rss1 > 0 + assert vms1 > 0 # step 2 - allocate some memory memarr = [None] * 1500000 @@ -597,19 +615,19 @@ def test_memory_info(self): percent2 = p.memory_percent() # step 3 - make sure that the memory usage bumped up - self.assertGreater(rss2, rss1) - self.assertGreaterEqual(vms2, vms1) # vms might be equal - self.assertGreater(percent2, percent1) + assert rss2 > rss1 + assert vms2 >= vms1 # vms might be equal + assert percent2 > percent1 del memarr if WINDOWS: mem = p.memory_info() - self.assertEqual(mem.rss, mem.wset) - self.assertEqual(mem.vms, mem.pagefile) + assert mem.rss == mem.wset + assert mem.vms == mem.pagefile mem = p.memory_info() for name in mem._fields: - self.assertGreaterEqual(getattr(mem, name), 0) + assert getattr(mem, name) >= 0 def test_memory_full_info(self): p = psutil.Process() @@ -617,21 +635,21 @@ def test_memory_full_info(self): mem = p.memory_full_info() for name in mem._fields: value = getattr(mem, name) - self.assertGreaterEqual(value, 0, msg=(name, value)) + assert value >= 0 if name == 'vms' and OSX or LINUX: continue - self.assertLessEqual(value, total, msg=(name, value, total)) + assert value <= total if LINUX or WINDOWS or MACOS: - self.assertGreaterEqual(mem.uss, 0) + assert mem.uss >= 0 if LINUX: - self.assertGreaterEqual(mem.pss, 0) - self.assertGreaterEqual(mem.swap, 0) + assert mem.pss >= 0 + assert mem.swap >= 0 - @unittest.skipIf(not HAS_MEMORY_MAPS, "not supported") + @pytest.mark.skipif(not HAS_MEMORY_MAPS, reason="not supported") def test_memory_maps(self): p = psutil.Process() maps = p.memory_maps() - self.assertEqual(len(maps), len(set(maps))) + assert len(maps) == len(set(maps)) ext_maps = p.memory_maps(grouped=False) for nt in maps: @@ -672,10 +690,10 @@ def test_memory_maps(self): if fname in ('addr', 'perms'): assert value, value else: - self.assertIsInstance(value, (int, long)) + assert isinstance(value, (int, long)) assert value >= 0, value - @unittest.skipIf(not HAS_MEMORY_MAPS, "not supported") + @pytest.mark.skipif(not HAS_MEMORY_MAPS, reason="not supported") def test_memory_maps_lists_lib(self): # Make sure a newly loaded shared lib is listed. p = psutil.Process() @@ -685,12 +703,13 @@ def normpath(p): return os.path.realpath(os.path.normcase(p)) libpaths = [normpath(x.path) for x in p.memory_maps()] - self.assertIn(normpath(path), libpaths) + assert normpath(path) in libpaths def test_memory_percent(self): p = psutil.Process() p.memory_percent() - self.assertRaises(ValueError, p.memory_percent, memtype="?!?") + with pytest.raises(ValueError): + p.memory_percent(memtype="?!?") if LINUX or MACOS or WINDOWS: p.memory_percent(memtype='uss') @@ -703,17 +722,17 @@ def test_is_running(self): assert not p.is_running() assert not p.is_running() - @unittest.skipIf(QEMU_USER, "QEMU user not supported") + @pytest.mark.skipif(QEMU_USER, reason="QEMU user not supported") def test_exe(self): p = self.spawn_psproc() exe = p.exe() try: - self.assertEqual(exe, PYTHON_EXE) + assert exe == PYTHON_EXE except AssertionError: if WINDOWS and len(exe) == len(PYTHON_EXE): # on Windows we don't care about case sensitivity normcase = os.path.normcase - self.assertEqual(normcase(exe), normcase(PYTHON_EXE)) + assert normcase(exe) == normcase(PYTHON_EXE) else: # certain platforms such as BSD are more accurate returning: # "/usr/local/bin/python2.7" @@ -723,15 +742,13 @@ def test_exe(self): # an error. ver = "%s.%s" % (sys.version_info[0], sys.version_info[1]) try: - self.assertEqual( - exe.replace(ver, ''), PYTHON_EXE.replace(ver, '') - ) + assert exe.replace(ver, '') == PYTHON_EXE.replace(ver, '') except AssertionError: # Typically MACOS. Really not sure what to do here. pass out = sh([exe, "-c", "import os; print('hey')"]) - self.assertEqual(out, 'hey') + assert out == 'hey' def test_cmdline(self): cmdline = [ @@ -743,7 +760,7 @@ def test_cmdline(self): if NETBSD and p.cmdline() == []: # https://github.com/giampaolo/psutil/issues/2250 - raise unittest.SkipTest("OPENBSD: returned EBUSY") + raise pytest.skip("OPENBSD: returned EBUSY") # XXX - most of the times the underlying sysctl() call on Net # and Open BSD returns a truncated string. @@ -751,21 +768,19 @@ def test_cmdline(self): # like this is a kernel bug. # XXX - AIX truncates long arguments in /proc/pid/cmdline if NETBSD or OPENBSD or AIX: - self.assertEqual(p.cmdline()[0], PYTHON_EXE) + assert p.cmdline()[0] == PYTHON_EXE else: if MACOS and CI_TESTING: pyexe = p.cmdline()[0] if pyexe != PYTHON_EXE: - self.assertEqual( - ' '.join(p.cmdline()[1:]), ' '.join(cmdline[1:]) - ) + assert ' '.join(p.cmdline()[1:]) == ' '.join(cmdline[1:]) return if QEMU_USER: - self.assertEqual(' '.join(p.cmdline()[2:]), ' '.join(cmdline)) + assert ' '.join(p.cmdline()[2:]) == ' '.join(cmdline) return - self.assertEqual(' '.join(p.cmdline()), ' '.join(cmdline)) + assert ' '.join(p.cmdline()) == ' '.join(cmdline) - @unittest.skipIf(PYPY, "broken on PYPY") + @pytest.mark.skipif(PYPY, reason="broken on PYPY") def test_long_cmdline(self): cmdline = [PYTHON_EXE] cmdline.extend(["-v"] * 50) @@ -777,17 +792,17 @@ def test_long_cmdline(self): # XXX: for some reason the test process may turn into a # zombie (don't know why). try: - self.assertEqual(p.cmdline(), cmdline) + assert p.cmdline() == cmdline except psutil.ZombieProcess: - raise unittest.SkipTest("OPENBSD: process turned into zombie") + raise pytest.skip("OPENBSD: process turned into zombie") elif QEMU_USER: - self.assertEqual(p.cmdline()[2:], cmdline) + assert p.cmdline()[2:] == cmdline else: ret = p.cmdline() if NETBSD and ret == []: # https://github.com/giampaolo/psutil/issues/2250 - raise unittest.SkipTest("OPENBSD: returned EBUSY") - self.assertEqual(ret, cmdline) + raise pytest.skip("OPENBSD: returned EBUSY") + assert ret == cmdline def test_name(self): p = self.spawn_psproc() @@ -795,8 +810,8 @@ def test_name(self): pyexe = os.path.basename(os.path.realpath(sys.executable)).lower() assert pyexe.startswith(name), (pyexe, name) - @unittest.skipIf(PYPY or QEMU_USER, "unreliable on PYPY") - @unittest.skipIf(QEMU_USER, "unreliable on QEMU user") + @pytest.mark.skipif(PYPY or QEMU_USER, reason="unreliable on PYPY") + @pytest.mark.skipif(QEMU_USER, reason="unreliable on QEMU user") def test_long_name(self): pyexe = create_py_exe(self.get_testfn(suffix="0123456789" * 2)) cmdline = [ @@ -814,20 +829,20 @@ def test_long_name(self): # just compare the first 15 chars. Full explanation: # https://github.com/giampaolo/psutil/issues/2239 try: - self.assertEqual(p.name(), os.path.basename(pyexe)) + assert p.name() == os.path.basename(pyexe) except AssertionError: if p.status() == psutil.STATUS_ZOMBIE: assert os.path.basename(pyexe).startswith(p.name()) else: raise else: - self.assertEqual(p.name(), os.path.basename(pyexe)) + assert p.name() == os.path.basename(pyexe) # XXX - @unittest.skipIf(SUNOS, "broken on SUNOS") - @unittest.skipIf(AIX, "broken on AIX") - @unittest.skipIf(PYPY, "broken on PYPY") - @unittest.skipIf(QEMU_USER, "broken on QEMU user") + @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(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: @@ -839,37 +854,37 @@ def test_prog_w_funky_name(self): "import time; [time.sleep(0.1) for x in range(100)]", ] p = self.spawn_psproc(cmdline) - self.assertEqual(p.cmdline(), cmdline) - self.assertEqual(p.name(), os.path.basename(pyexe)) - self.assertEqual(os.path.normcase(p.exe()), os.path.normcase(pyexe)) + assert p.cmdline() == cmdline + assert p.name() == os.path.basename(pyexe) + assert os.path.normcase(p.exe()) == os.path.normcase(pyexe) - @unittest.skipIf(not POSIX, 'POSIX only') + @pytest.mark.skipif(not POSIX, reason="POSIX only") def test_uids(self): p = psutil.Process() real, effective, _saved = p.uids() # os.getuid() refers to "real" uid - self.assertEqual(real, os.getuid()) + assert real == os.getuid() # os.geteuid() refers to "effective" uid - self.assertEqual(effective, os.geteuid()) + 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. if hasattr(os, "getresuid"): - self.assertEqual(os.getresuid(), p.uids()) + assert os.getresuid() == p.uids() - @unittest.skipIf(not POSIX, 'POSIX only') + @pytest.mark.skipif(not POSIX, reason="POSIX only") def test_gids(self): p = psutil.Process() real, effective, _saved = p.gids() # os.getuid() refers to "real" uid - self.assertEqual(real, os.getgid()) + assert real == os.getgid() # os.geteuid() refers to "effective" uid - self.assertEqual(effective, os.getegid()) + 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. if hasattr(os, "getresuid"): - self.assertEqual(os.getresgid(), p.gids()) + assert os.getresgid() == p.gids() def test_nice(self): def cleanup(init): @@ -879,7 +894,8 @@ def cleanup(init): pass p = psutil.Process() - self.assertRaises(TypeError, p.nice, "str") + with pytest.raises(TypeError): + p.nice("str") init = p.nice() self.addCleanup(cleanup, init) @@ -911,33 +927,35 @@ def cleanup(init): ): if new_prio == prio or highest_prio is None: highest_prio = prio - self.assertEqual(new_prio, highest_prio) + assert new_prio == highest_prio else: - self.assertEqual(new_prio, prio) + assert new_prio == prio else: try: if hasattr(os, "getpriority"): - self.assertEqual( - os.getpriority(os.PRIO_PROCESS, os.getpid()), p.nice() + assert ( + os.getpriority(os.PRIO_PROCESS, os.getpid()) + == p.nice() ) p.nice(1) - self.assertEqual(p.nice(), 1) + assert p.nice() == 1 if hasattr(os, "getpriority"): - self.assertEqual( - os.getpriority(os.PRIO_PROCESS, os.getpid()), p.nice() + assert ( + os.getpriority(os.PRIO_PROCESS, os.getpid()) + == p.nice() ) # XXX - going back to previous nice value raises # AccessDenied on MACOS if not MACOS: p.nice(0) - self.assertEqual(p.nice(), 0) + assert p.nice() == 0 except psutil.AccessDenied: pass - @unittest.skipIf(QEMU_USER, "QEMU user not supported") + @pytest.mark.skipif(QEMU_USER, reason="QEMU user not supported") def test_status(self): p = psutil.Process() - self.assertEqual(p.status(), psutil.STATUS_RUNNING) + assert p.status() == psutil.STATUS_RUNNING def test_username(self): p = self.spawn_psproc() @@ -949,16 +967,16 @@ def test_username(self): # When running as a service account (most likely to be # NetworkService), these user name calculations don't produce # the same result, causing the test to fail. - raise unittest.SkipTest('running as service account') - self.assertEqual(username, getpass_user) + raise pytest.skip('running as service account') + assert username == getpass_user if 'USERDOMAIN' in os.environ: - self.assertEqual(domain, os.environ['USERDOMAIN']) + assert domain == os.environ['USERDOMAIN'] else: - self.assertEqual(username, getpass.getuser()) + assert username == getpass.getuser() def test_cwd(self): p = self.spawn_psproc() - self.assertEqual(p.cwd(), os.getcwd()) + assert p.cwd() == os.getcwd() def test_cwd_2(self): cmd = [ @@ -970,9 +988,9 @@ def test_cwd_2(self): ), ] p = self.spawn_psproc(cmd) - call_until(p.cwd, "ret == os.path.dirname(os.getcwd())") + call_until(lambda: p.cwd() == os.path.dirname(os.getcwd())) - @unittest.skipIf(not HAS_CPU_AFFINITY, 'not supported') + @pytest.mark.skipif(not HAS_CPU_AFFINITY, reason="not supported") def test_cpu_affinity(self): p = psutil.Process() initial = p.cpu_affinity() @@ -980,50 +998,51 @@ def test_cpu_affinity(self): self.addCleanup(p.cpu_affinity, initial) if hasattr(os, "sched_getaffinity"): - self.assertEqual(initial, list(os.sched_getaffinity(p.pid))) - self.assertEqual(len(initial), len(set(initial))) + assert initial == list(os.sched_getaffinity(p.pid)) + assert len(initial) == len(set(initial)) all_cpus = list(range(len(psutil.cpu_percent(percpu=True)))) for n in all_cpus: p.cpu_affinity([n]) - self.assertEqual(p.cpu_affinity(), [n]) + assert p.cpu_affinity() == [n] if hasattr(os, "sched_getaffinity"): - self.assertEqual( - p.cpu_affinity(), list(os.sched_getaffinity(p.pid)) - ) + assert p.cpu_affinity() == list(os.sched_getaffinity(p.pid)) # also test num_cpu() if hasattr(p, "num_cpu"): - self.assertEqual(p.cpu_affinity()[0], p.num_cpu()) + assert p.cpu_affinity()[0] == p.num_cpu() # [] is an alias for "all eligible CPUs"; on Linux this may # not be equal to all available CPUs, see: # https://github.com/giampaolo/psutil/issues/956 p.cpu_affinity([]) if LINUX: - self.assertEqual(p.cpu_affinity(), p._proc._get_eligible_cpus()) + assert p.cpu_affinity() == p._proc._get_eligible_cpus() else: - self.assertEqual(p.cpu_affinity(), all_cpus) + assert p.cpu_affinity() == all_cpus if hasattr(os, "sched_getaffinity"): - self.assertEqual( - p.cpu_affinity(), list(os.sched_getaffinity(p.pid)) - ) + assert p.cpu_affinity() == list(os.sched_getaffinity(p.pid)) - self.assertRaises(TypeError, p.cpu_affinity, 1) + with pytest.raises(TypeError): + p.cpu_affinity(1) p.cpu_affinity(initial) # it should work with all iterables, not only lists p.cpu_affinity(set(all_cpus)) p.cpu_affinity(tuple(all_cpus)) - @unittest.skipIf(not HAS_CPU_AFFINITY, 'not supported') + @pytest.mark.skipif(not HAS_CPU_AFFINITY, reason="not supported") def test_cpu_affinity_errs(self): p = self.spawn_psproc() invalid_cpu = [len(psutil.cpu_times(percpu=True)) + 10] - self.assertRaises(ValueError, p.cpu_affinity, invalid_cpu) - self.assertRaises(ValueError, p.cpu_affinity, range(10000, 11000)) - self.assertRaises(TypeError, p.cpu_affinity, [0, "1"]) - self.assertRaises(ValueError, p.cpu_affinity, [0, -1]) - - @unittest.skipIf(not HAS_CPU_AFFINITY, 'not supported') + with pytest.raises(ValueError): + p.cpu_affinity(invalid_cpu) + with pytest.raises(ValueError): + p.cpu_affinity(range(10000, 11000)) + with pytest.raises(TypeError): + p.cpu_affinity([0, "1"]) + with pytest.raises(ValueError): + p.cpu_affinity([0, -1]) + + @pytest.mark.skipif(not HAS_CPU_AFFINITY, reason="not supported") def test_cpu_affinity_all_combinations(self): p = psutil.Process() initial = p.cpu_affinity() @@ -1041,28 +1060,29 @@ def test_cpu_affinity_all_combinations(self): for combo in combos: p.cpu_affinity(combo) - self.assertEqual(sorted(p.cpu_affinity()), sorted(combo)) + assert sorted(p.cpu_affinity()) == sorted(combo) # TODO: #595 - @unittest.skipIf(BSD, "broken on BSD") + @pytest.mark.skipif(BSD, reason="broken on BSD") # can't find any process file on Appveyor - @unittest.skipIf(APPVEYOR, "unreliable on APPVEYOR") + @pytest.mark.skipif(APPVEYOR, reason="unreliable on APPVEYOR") def test_open_files(self): p = psutil.Process() testfn = self.get_testfn() files = p.open_files() - self.assertNotIn(testfn, files) + assert testfn not in files with open(testfn, 'wb') as f: f.write(b'x' * 1024) f.flush() # give the kernel some time to see the new file - files = call_until(p.open_files, "len(ret) != %i" % len(files)) + call_until(lambda: len(p.open_files()) != len(files)) + files = p.open_files() filenames = [os.path.normcase(x.path) for x in files] - self.assertIn(os.path.normcase(testfn), filenames) + assert os.path.normcase(testfn) in filenames if LINUX: for file in files: if file.path == testfn: - self.assertEqual(file.position, 1024) + assert file.position == 1024 for file in files: assert os.path.isfile(file.path), file @@ -1079,14 +1099,14 @@ def test_open_files(self): break time.sleep(0.01) else: - self.assertIn(os.path.normcase(testfn), filenames) + assert os.path.normcase(testfn) in filenames for file in filenames: assert os.path.isfile(file), file # TODO: #595 - @unittest.skipIf(BSD, "broken on BSD") + @pytest.mark.skipif(BSD, reason="broken on BSD") # can't find any process file on Appveyor - @unittest.skipIf(APPVEYOR, "unreliable on APPVEYOR") + @pytest.mark.skipif(APPVEYOR, reason="unreliable on APPVEYOR") def test_open_files_2(self): # test fd and path fields p = psutil.Process() @@ -1103,35 +1123,37 @@ def test_open_files_2(self): raise self.fail( "no file found; files=%s" % (repr(p.open_files())) ) - self.assertEqual(normcase(file.path), normcase(fileobj.name)) + assert normcase(file.path) == normcase(fileobj.name) if WINDOWS: - self.assertEqual(file.fd, -1) + assert file.fd == -1 else: - self.assertEqual(file.fd, fileobj.fileno()) + assert file.fd == fileobj.fileno() # test positions ntuple = p.open_files()[0] - self.assertEqual(ntuple[0], ntuple.path) - self.assertEqual(ntuple[1], ntuple.fd) + assert ntuple[0] == ntuple.path + assert ntuple[1] == ntuple.fd # test file is gone - self.assertNotIn(fileobj.name, p.open_files()) + assert fileobj.name not in p.open_files() - @unittest.skipIf(not POSIX, 'POSIX only') + @pytest.mark.skipif(not POSIX, reason="POSIX only") def test_num_fds(self): p = psutil.Process() testfn = self.get_testfn() start = p.num_fds() file = open(testfn, 'w') self.addCleanup(file.close) - self.assertEqual(p.num_fds(), start + 1) + assert p.num_fds() == start + 1 sock = socket.socket() self.addCleanup(sock.close) - self.assertEqual(p.num_fds(), start + 2) + assert p.num_fds() == start + 2 file.close() sock.close() - self.assertEqual(p.num_fds(), start) + assert p.num_fds() == start @skip_on_not_implemented(only_if=LINUX) - @unittest.skipIf(OPENBSD or NETBSD, "not reliable on OPENBSD & NETBSD") + @pytest.mark.skipif( + OPENBSD or NETBSD, reason="not reliable on OPENBSD & NETBSD" + ) def test_num_ctx_switches(self): p = psutil.Process() before = sum(p.num_ctx_switches()) @@ -1145,37 +1167,37 @@ def test_num_ctx_switches(self): def test_ppid(self): p = psutil.Process() if hasattr(os, 'getppid'): - self.assertEqual(p.ppid(), os.getppid()) + assert p.ppid() == os.getppid() p = self.spawn_psproc() - self.assertEqual(p.ppid(), os.getpid()) + assert p.ppid() == os.getpid() def test_parent(self): p = self.spawn_psproc() - self.assertEqual(p.parent().pid, os.getpid()) + assert p.parent().pid == os.getpid() lowest_pid = psutil.pids()[0] - self.assertIsNone(psutil.Process(lowest_pid).parent()) + assert psutil.Process(lowest_pid).parent() is None def test_parent_multi(self): parent = psutil.Process() child, grandchild = self.spawn_children_pair() - self.assertEqual(grandchild.parent(), child) - self.assertEqual(child.parent(), parent) + assert grandchild.parent() == child + assert child.parent() == parent - @unittest.skipIf(QEMU_USER, "QEMU user not supported") + @pytest.mark.skipif(QEMU_USER, reason="QEMU user not supported") @retry_on_failure() def test_parents(self): parent = psutil.Process() assert parent.parents() child, grandchild = self.spawn_children_pair() - self.assertEqual(child.parents()[0], parent) - self.assertEqual(grandchild.parents()[0], child) - self.assertEqual(grandchild.parents()[1], parent) + assert child.parents()[0] == parent + assert grandchild.parents()[0] == child + assert grandchild.parents()[1] == parent def test_children(self): parent = psutil.Process() - self.assertEqual(parent.children(), []) - self.assertEqual(parent.children(recursive=True), []) + assert parent.children() == [] + assert parent.children(recursive=True) == [] # On Windows we set the flag to 0 in order to cancel out the # CREATE_NO_WINDOW flag (enabled by default) which creates # an extra "conhost.exe" child. @@ -1183,22 +1205,22 @@ def test_children(self): children1 = parent.children() children2 = parent.children(recursive=True) for children in (children1, children2): - self.assertEqual(len(children), 1) - self.assertEqual(children[0].pid, child.pid) - self.assertEqual(children[0].ppid(), parent.pid) + assert len(children) == 1 + assert children[0].pid == child.pid + assert children[0].ppid() == parent.pid def test_children_recursive(self): # Test children() against two sub processes, p1 and p2, where # p1 (our child) spawned p2 (our grandchild). parent = psutil.Process() child, grandchild = self.spawn_children_pair() - self.assertEqual(parent.children(), [child]) - self.assertEqual(parent.children(recursive=True), [child, grandchild]) + assert parent.children() == [child] + assert parent.children(recursive=True) == [child, grandchild] # If the intermediate process is gone there's no way for # children() to recursively find it. child.terminate() child.wait() - self.assertEqual(parent.children(recursive=True), []) + assert parent.children(recursive=True) == [] def test_children_duplicates(self): # find the process which has the highest number of children @@ -1211,27 +1233,27 @@ def test_children_duplicates(self): # this is the one, now let's make sure there are no duplicates pid = sorted(table.items(), key=lambda x: x[1])[-1][0] if LINUX and pid == 0: - raise unittest.SkipTest("PID 0") + raise pytest.skip("PID 0") p = psutil.Process(pid) try: c = p.children(recursive=True) except psutil.AccessDenied: # windows pass else: - self.assertEqual(len(c), len(set(c))) + assert len(c) == len(set(c)) def test_parents_and_children(self): parent = psutil.Process() child, grandchild = self.spawn_children_pair() # forward children = parent.children(recursive=True) - self.assertEqual(len(children), 2) - self.assertEqual(children[0], child) - self.assertEqual(children[1], grandchild) + assert len(children) == 2 + assert children[0] == child + assert children[1] == grandchild # backward parents = grandchild.parents() - self.assertEqual(parents[0], child) - self.assertEqual(parents[1], parent) + assert parents[0] == child + assert parents[1] == parent def test_suspend_resume(self): p = self.spawn_psproc() @@ -1241,29 +1263,29 @@ def test_suspend_resume(self): break time.sleep(0.01) p.resume() - self.assertNotEqual(p.status(), psutil.STATUS_STOPPED) + assert p.status() != psutil.STATUS_STOPPED def test_invalid_pid(self): - self.assertRaises(TypeError, psutil.Process, "1") - self.assertRaises(ValueError, psutil.Process, -1) + with pytest.raises(TypeError): + psutil.Process("1") + with pytest.raises(ValueError): + psutil.Process(-1) def test_as_dict(self): p = psutil.Process() d = p.as_dict(attrs=['exe', 'name']) - self.assertEqual(sorted(d.keys()), ['exe', 'name']) + assert sorted(d.keys()) == ['exe', 'name'] p = psutil.Process(min(psutil.pids())) d = p.as_dict(attrs=['net_connections'], ad_value='foo') if not isinstance(d['net_connections'], list): - self.assertEqual(d['net_connections'], 'foo') + assert d['net_connections'] == 'foo' # Test ad_value is set on AccessDenied. with mock.patch( 'psutil.Process.nice', create=True, side_effect=psutil.AccessDenied ): - self.assertEqual( - p.as_dict(attrs=["nice"], ad_value=1), {"nice": 1} - ) + assert p.as_dict(attrs=["nice"], ad_value=1) == {"nice": 1} # Test that NoSuchProcess bubbles up. with mock.patch( @@ -1271,7 +1293,8 @@ def test_as_dict(self): create=True, side_effect=psutil.NoSuchProcess(p.pid, "name"), ): - self.assertRaises(psutil.NoSuchProcess, p.as_dict, attrs=["nice"]) + with pytest.raises(psutil.NoSuchProcess): + p.as_dict(attrs=["nice"]) # Test that ZombieProcess is swallowed. with mock.patch( @@ -1279,9 +1302,7 @@ def test_as_dict(self): create=True, side_effect=psutil.ZombieProcess(p.pid, "name"), ): - self.assertEqual( - p.as_dict(attrs=["nice"], ad_value="foo"), {"nice": "foo"} - ) + assert p.as_dict(attrs=["nice"], ad_value="foo") == {"nice": "foo"} # By default APIs raising NotImplementedError are # supposed to be skipped. @@ -1289,17 +1310,17 @@ def test_as_dict(self): 'psutil.Process.nice', create=True, side_effect=NotImplementedError ): d = p.as_dict() - self.assertNotIn('nice', list(d.keys())) + assert 'nice' not in list(d.keys()) # ...unless the user explicitly asked for some attr. - with self.assertRaises(NotImplementedError): + with pytest.raises(NotImplementedError): p.as_dict(attrs=["nice"]) # errors - with self.assertRaises(TypeError): + with pytest.raises(TypeError): p.as_dict('name') - with self.assertRaises(ValueError): + with pytest.raises(ValueError): p.as_dict(['foo']) - with self.assertRaises(ValueError): + with pytest.raises(ValueError): p.as_dict(['foo', 'bar']) def test_oneshot(self): @@ -1308,12 +1329,12 @@ def test_oneshot(self): with p.oneshot(): p.cpu_times() p.cpu_times() - self.assertEqual(m.call_count, 1) + assert m.call_count == 1 with mock.patch("psutil._psplatform.Process.cpu_times") as m: p.cpu_times() p.cpu_times() - self.assertEqual(m.call_count, 2) + assert m.call_count == 2 def test_oneshot_twice(self): # Test the case where the ctx manager is __enter__ed twice. @@ -1327,13 +1348,13 @@ def test_oneshot_twice(self): with p.oneshot(): p.cpu_times() p.cpu_times() - self.assertEqual(m1.call_count, 1) - self.assertEqual(m2.call_count, 1) + assert m1.call_count == 1 + assert m2.call_count == 1 with mock.patch("psutil._psplatform.Process.cpu_times") as m: p.cpu_times() p.cpu_times() - self.assertEqual(m.call_count, 2) + assert m.call_count == 2 def test_oneshot_cache(self): # Make sure oneshot() cache is nonglobal. Instead it's @@ -1342,13 +1363,13 @@ def test_oneshot_cache(self): p1, p2 = self.spawn_children_pair() p1_ppid = p1.ppid() p2_ppid = p2.ppid() - self.assertNotEqual(p1_ppid, p2_ppid) + assert p1_ppid != p2_ppid with p1.oneshot(): - self.assertEqual(p1.ppid(), p1_ppid) - self.assertEqual(p2.ppid(), p2_ppid) + assert p1.ppid() == p1_ppid + assert p2.ppid() == p2_ppid with p2.oneshot(): - self.assertEqual(p1.ppid(), p1_ppid) - self.assertEqual(p2.ppid(), p2_ppid) + assert p1.ppid() == p1_ppid + assert p2.ppid() == p2_ppid def test_halfway_terminated_process(self): # Test that NoSuchProcess exception gets raised in case the @@ -1381,19 +1402,19 @@ def assert_raises_nsp(fun, fun_name): p.terminate() p.wait() if WINDOWS: # XXX - call_until(psutil.pids, "%s not in ret" % p.pid) + call_until(lambda: p.pid not in psutil.pids()) self.assertProcessGone(p) ns = process_namespace(p) for fun, name in ns.iter(ns.all): assert_raises_nsp(fun, name) - @unittest.skipIf(not POSIX, 'POSIX only') + @pytest.mark.skipif(not POSIX, reason="POSIX only") def test_zombie_process(self): _parent, zombie = self.spawn_zombie() self.assertProcessZombie(zombie) - @unittest.skipIf(not POSIX, 'POSIX only') + @pytest.mark.skipif(not POSIX, reason="POSIX only") def test_zombie_process_is_running_w_exc(self): # Emulate a case where internally is_running() raises # ZombieProcess. @@ -1404,7 +1425,7 @@ def test_zombie_process_is_running_w_exc(self): assert p.is_running() assert m.called - @unittest.skipIf(not POSIX, 'POSIX only') + @pytest.mark.skipif(not POSIX, reason="POSIX only") def test_zombie_process_status_w_exc(self): # Emulate a case where internally status() raises # ZombieProcess. @@ -1413,7 +1434,7 @@ def test_zombie_process_status_w_exc(self): "psutil._psplatform.Process.status", side_effect=psutil.ZombieProcess(0), ) as m: - self.assertEqual(p.status(), psutil.STATUS_ZOMBIE) + assert p.status() == psutil.STATUS_ZOMBIE assert m.called def test_reused_pid(self): @@ -1428,52 +1449,65 @@ def test_reused_pid(self): p._ident = (p.pid, p.create_time() + 100) list(psutil.process_iter()) - self.assertIn(p.pid, psutil._pmap) + assert p.pid in psutil._pmap assert not p.is_running() # make sure is_running() removed PID from process_iter() # internal cache - with redirect_stderr(StringIO()) as f: - list(psutil.process_iter()) - self.assertIn( - "refreshing Process instance for reused PID %s" % p.pid, - f.getvalue(), + with mock.patch.object(psutil._common, "PSUTIL_DEBUG", True): + with redirect_stderr(StringIO()) as f: + list(psutil.process_iter()) + assert ( + "refreshing Process instance for reused PID %s" % p.pid + in f.getvalue() ) - self.assertNotIn(p.pid, psutil._pmap) + assert p.pid not in psutil._pmap assert p != psutil.Process(subp.pid) msg = "process no longer exists and its PID has been reused" ns = process_namespace(p) for fun, name in ns.iter(ns.setters + ns.killers, clear_cache=False): with self.subTest(name=name): - self.assertRaisesRegex(psutil.NoSuchProcess, msg, fun) + with pytest.raises(psutil.NoSuchProcess, match=msg): + fun() - self.assertIn("terminated + PID reused", str(p)) - self.assertIn("terminated + PID reused", repr(p)) + assert "terminated + PID reused" in str(p) + assert "terminated + PID reused" in repr(p) - self.assertRaisesRegex(psutil.NoSuchProcess, msg, p.ppid) - self.assertRaisesRegex(psutil.NoSuchProcess, msg, p.parent) - self.assertRaisesRegex(psutil.NoSuchProcess, msg, p.parents) - self.assertRaisesRegex(psutil.NoSuchProcess, msg, p.children) + with pytest.raises(psutil.NoSuchProcess, match=msg): + p.ppid() + with pytest.raises(psutil.NoSuchProcess, match=msg): + p.parent() + with pytest.raises(psutil.NoSuchProcess, match=msg): + p.parents() + with pytest.raises(psutil.NoSuchProcess, match=msg): + p.children() def test_pid_0(self): # Process(0) is supposed to work on all platforms except Linux if 0 not in psutil.pids(): - self.assertRaises(psutil.NoSuchProcess, psutil.Process, 0) + with pytest.raises(psutil.NoSuchProcess): + psutil.Process(0) # These 2 are a contradiction, but "ps" says PID 1's parent # is PID 0. assert not psutil.pid_exists(0) - self.assertEqual(psutil.Process(1).ppid(), 0) + assert psutil.Process(1).ppid() == 0 return p = psutil.Process(0) exc = psutil.AccessDenied if WINDOWS else ValueError - self.assertRaises(exc, p.wait) - self.assertRaises(exc, p.terminate) - self.assertRaises(exc, p.suspend) - self.assertRaises(exc, p.resume) - self.assertRaises(exc, p.kill) - self.assertRaises(exc, p.send_signal, signal.SIGTERM) + with pytest.raises(exc): + p.wait() + with pytest.raises(exc): + p.terminate() + with pytest.raises(exc): + p.suspend() + with pytest.raises(exc): + p.resume() + with pytest.raises(exc): + p.kill() + with pytest.raises(exc): + p.send_signal(signal.SIGTERM) # test all methods ns = process_namespace(p) @@ -1484,27 +1518,30 @@ def test_pid_0(self): pass else: if name in ("uids", "gids"): - self.assertEqual(ret.real, 0) + assert ret.real == 0 elif name == "username": user = 'NT AUTHORITY\\SYSTEM' if WINDOWS else 'root' - self.assertEqual(p.username(), user) + assert p.username() == user elif name == "name": assert name, name if not OPENBSD: - self.assertIn(0, psutil.pids()) + assert 0 in psutil.pids() assert psutil.pid_exists(0) - @unittest.skipIf(not HAS_ENVIRON, "not supported") + @pytest.mark.skipif(not HAS_ENVIRON, reason="not supported") def test_environ(self): def clean_dict(d): - # Most of these are problematic on Travis. - d.pop("PLAT", None) - d.pop("HOME", None) + exclude = ["PLAT", "HOME", "PYTEST_CURRENT_TEST", "PYTEST_VERSION"] if MACOS: - d.pop("__CF_USER_TEXT_ENCODING", None) - d.pop("VERSIONER_PYTHON_PREFER_32_BIT", None) - d.pop("VERSIONER_PYTHON_VERSION", None) + exclude.extend([ + "__CF_USER_TEXT_ENCODING", + "VERSIONER_PYTHON_PREFER_32_BIT", + "VERSIONER_PYTHON_VERSION", + "VERSIONER_PYTHON_VERSION", + ]) + for name in exclude: + d.pop(name, None) return dict([ ( k.replace("\r", "").replace("\n", ""), @@ -1518,15 +1555,17 @@ def clean_dict(d): d1 = clean_dict(p.environ()) d2 = clean_dict(os.environ.copy()) if not OSX and GITHUB_ACTIONS: - self.assertEqual(d1, d2) + assert d1 == d2 - @unittest.skipIf(not HAS_ENVIRON, "not supported") - @unittest.skipIf(not POSIX, "POSIX only") - @unittest.skipIf( + @pytest.mark.skipif(not HAS_ENVIRON, reason="not supported") + @pytest.mark.skipif(not POSIX, reason="POSIX only") + @pytest.mark.skipif( MACOS_11PLUS, - "macOS 11+ can't get another process environment, issue #2084", + reason="macOS 11+ can't get another process environment, issue #2084", + ) + @pytest.mark.skipif( + NETBSD, reason="sometimes fails on `assert is_running()`" ) - @unittest.skipIf(NETBSD, "sometimes fails on `assert is_running()`") def test_weird_environ(self): # environment variables can contain values without an equals sign code = textwrap.dedent(""" @@ -1552,7 +1591,7 @@ def test_weird_environ(self): wait_for_pid(p.pid) assert p.is_running() # Wait for process to exec or exit. - self.assertEqual(sproc.stderr.read(), b"") + assert sproc.stderr.read() == b"" if MACOS and CI_TESTING: try: env = p.environ() @@ -1562,9 +1601,9 @@ def test_weird_environ(self): return else: env = p.environ() - self.assertEqual(env, {"A": "1", "C": "3"}) + assert env == {"A": "1", "C": "3"} sproc.communicate() - self.assertEqual(sproc.returncode, 0) + assert sproc.returncode == 0 # =================================================================== @@ -1618,7 +1657,7 @@ def test_nice(self): else: raise self.fail("exception not raised") - @unittest.skipIf(1, "causes problem as root") + @pytest.mark.skipif(True, reason="causes problem as root") def test_zombie_process(self): pass @@ -1653,13 +1692,14 @@ def test_misc(self): proc.name() proc.cpu_times() proc.stdin # noqa - self.assertTrue(dir(proc)) - self.assertRaises(AttributeError, getattr, proc, 'foo') + assert dir(proc) + with pytest.raises(AttributeError): + proc.foo # noqa proc.terminate() if POSIX: - self.assertEqual(proc.wait(5), -signal.SIGTERM) + assert proc.wait(5) == -signal.SIGTERM else: - self.assertEqual(proc.wait(5), signal.SIGTERM) + assert proc.wait(5) == signal.SIGTERM def test_ctx_manager(self): with psutil.Popen( @@ -1673,7 +1713,7 @@ def test_ctx_manager(self): assert proc.stdout.closed assert proc.stderr.closed assert proc.stdin.closed - self.assertEqual(proc.returncode, 0) + assert proc.returncode == 0 def test_kill_terminate(self): # subprocess.Popen()'s terminate(), kill() and send_signal() do @@ -1692,23 +1732,14 @@ def test_kill_terminate(self): ) as proc: proc.terminate() proc.wait() - self.assertRaises(psutil.NoSuchProcess, proc.terminate) - self.assertRaises(psutil.NoSuchProcess, proc.kill) - self.assertRaises( - psutil.NoSuchProcess, proc.send_signal, signal.SIGTERM - ) + with pytest.raises(psutil.NoSuchProcess): + proc.terminate() + with pytest.raises(psutil.NoSuchProcess): + proc.kill() + with pytest.raises(psutil.NoSuchProcess): + proc.send_signal(signal.SIGTERM) if WINDOWS: - self.assertRaises( - psutil.NoSuchProcess, proc.send_signal, signal.CTRL_C_EVENT - ) - self.assertRaises( - psutil.NoSuchProcess, - proc.send_signal, - signal.CTRL_BREAK_EVENT, - ) - - -if __name__ == '__main__': - from psutil.tests.runner import run_from_name - - run_from_name(__file__) + with pytest.raises(psutil.NoSuchProcess): + proc.send_signal(signal.CTRL_C_EVENT) + with pytest.raises(psutil.NoSuchProcess): + proc.send_signal(signal.CTRL_BREAK_EVENT) diff --git a/psutil/tests/test_process_all.py b/psutil/tests/test_process_all.py index 48833a105..1550046ba 100755 --- a/psutil/tests/test_process_all.py +++ b/psutil/tests/test_process_all.py @@ -32,6 +32,7 @@ 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 from psutil.tests import VALID_PROC_STATUSES from psutil.tests import PsutilTestCase @@ -40,12 +41,12 @@ from psutil.tests import is_namedtuple from psutil.tests import is_win_secure_system_proc from psutil.tests import process_namespace -from psutil.tests import serialrun +from psutil.tests import pytest # Cuts the time in half, but (e.g.) on macOS the process pool stays # alive after join() (multiprocessing bug?), messing up other tests. -USE_PROC_POOL = LINUX and not CI_TESTING +USE_PROC_POOL = LINUX and not CI_TESTING and not PYTEST_PARALLEL def proc_info(pid): @@ -97,7 +98,6 @@ def do_wait(): return info -@serialrun class TestFetchAllProcesses(PsutilTestCase): """Test which iterates over all running processes and performs some sanity checks against Process API's returned values. @@ -156,13 +156,13 @@ def test_all(self): raise self.fail(''.join(failures)) def cmdline(self, ret, info): - self.assertIsInstance(ret, list) + assert isinstance(ret, list) for part in ret: - self.assertIsInstance(part, str) + assert isinstance(part, str) def exe(self, ret, info): - self.assertIsInstance(ret, (str, unicode)) - self.assertEqual(ret.strip(), ret) + assert isinstance(ret, (str, unicode)) + assert ret.strip() == ret if ret: if WINDOWS and not ret.endswith('.exe'): return # May be "Registry", "MemCompression", ... @@ -180,16 +180,16 @@ def exe(self, ret, info): raise def pid(self, ret, info): - self.assertIsInstance(ret, int) - self.assertGreaterEqual(ret, 0) + assert isinstance(ret, int) + assert ret >= 0 def ppid(self, ret, info): - self.assertIsInstance(ret, (int, long)) - self.assertGreaterEqual(ret, 0) + assert isinstance(ret, (int, long)) + assert ret >= 0 proc_info(ret) def name(self, ret, info): - self.assertIsInstance(ret, (str, unicode)) + assert isinstance(ret, (str, unicode)) if WINDOWS and not ret and is_win_secure_system_proc(info['pid']): # https://github.com/giampaolo/psutil/issues/2338 return @@ -198,9 +198,9 @@ def name(self, ret, info): assert ret, repr(ret) def create_time(self, ret, info): - self.assertIsInstance(ret, float) + assert isinstance(ret, float) try: - self.assertGreaterEqual(ret, 0) + assert ret >= 0 except AssertionError: # XXX if OPENBSD and info['status'] == psutil.STATUS_ZOMBIE: @@ -216,45 +216,45 @@ def create_time(self, ret, info): def uids(self, ret, info): assert is_namedtuple(ret) for uid in ret: - self.assertIsInstance(uid, int) - self.assertGreaterEqual(uid, 0) + assert isinstance(uid, int) + assert uid >= 0 def gids(self, ret, info): assert is_namedtuple(ret) # note: testing all gids as above seems not to be reliable for # gid == 30 (nodoby); not sure why. for gid in ret: - self.assertIsInstance(gid, int) + assert isinstance(gid, int) if not MACOS and not NETBSD: - self.assertGreaterEqual(gid, 0) + assert gid >= 0 def username(self, ret, info): - self.assertIsInstance(ret, str) - self.assertEqual(ret.strip(), ret) + assert isinstance(ret, str) + assert ret.strip() == ret assert ret.strip() def status(self, ret, info): - self.assertIsInstance(ret, str) + assert isinstance(ret, str) assert ret, ret if QEMU_USER: # status does not work under qemu user return - self.assertNotEqual(ret, '?') # XXX - self.assertIn(ret, VALID_PROC_STATUSES) + assert ret != '?' # XXX + assert ret in VALID_PROC_STATUSES def io_counters(self, ret, info): assert is_namedtuple(ret) for field in ret: - self.assertIsInstance(field, (int, long)) + assert isinstance(field, (int, long)) if field != -1: - self.assertGreaterEqual(field, 0) + assert field >= 0 def ionice(self, ret, info): if LINUX: - self.assertIsInstance(ret.ioclass, int) - self.assertIsInstance(ret.value, int) - self.assertGreaterEqual(ret.ioclass, 0) - self.assertGreaterEqual(ret.value, 0) + assert isinstance(ret.ioclass, int) + assert isinstance(ret.value, int) + assert ret.ioclass >= 0 + assert ret.value >= 0 else: # Windows, Cygwin choices = [ psutil.IOPRIO_VERYLOW, @@ -262,89 +262,89 @@ def ionice(self, ret, info): psutil.IOPRIO_NORMAL, psutil.IOPRIO_HIGH, ] - self.assertIsInstance(ret, int) - self.assertGreaterEqual(ret, 0) - self.assertIn(ret, choices) + assert isinstance(ret, int) + assert ret >= 0 + assert ret in choices def num_threads(self, ret, info): - self.assertIsInstance(ret, int) + assert isinstance(ret, int) if WINDOWS and ret == 0 and is_win_secure_system_proc(info['pid']): # https://github.com/giampaolo/psutil/issues/2338 return - self.assertGreaterEqual(ret, 1) + assert ret >= 1 def threads(self, ret, info): - self.assertIsInstance(ret, list) + assert isinstance(ret, list) for t in ret: assert is_namedtuple(t) - self.assertGreaterEqual(t.id, 0) - self.assertGreaterEqual(t.user_time, 0) - self.assertGreaterEqual(t.system_time, 0) + assert t.id >= 0 + assert t.user_time >= 0 + assert t.system_time >= 0 for field in t: - self.assertIsInstance(field, (int, float)) + assert isinstance(field, (int, float)) def cpu_times(self, ret, info): assert is_namedtuple(ret) for n in ret: - self.assertIsInstance(n, float) - self.assertGreaterEqual(n, 0) + assert isinstance(n, float) + assert n >= 0 # TODO: check ntuple fields def cpu_percent(self, ret, info): - self.assertIsInstance(ret, float) + assert isinstance(ret, float) assert 0.0 <= ret <= 100.0, ret def cpu_num(self, ret, info): - self.assertIsInstance(ret, int) + assert isinstance(ret, int) if FREEBSD and ret == -1: return - self.assertGreaterEqual(ret, 0) + assert ret >= 0 if psutil.cpu_count() == 1: - self.assertEqual(ret, 0) - self.assertIn(ret, list(range(psutil.cpu_count()))) + assert ret == 0 + assert ret in list(range(psutil.cpu_count())) def memory_info(self, ret, info): assert is_namedtuple(ret) for value in ret: - self.assertIsInstance(value, (int, long)) - self.assertGreaterEqual(value, 0) + assert isinstance(value, (int, long)) + assert value >= 0 if WINDOWS: - self.assertGreaterEqual(ret.peak_wset, ret.wset) - self.assertGreaterEqual(ret.peak_paged_pool, ret.paged_pool) - self.assertGreaterEqual(ret.peak_nonpaged_pool, ret.nonpaged_pool) - self.assertGreaterEqual(ret.peak_pagefile, ret.pagefile) + assert ret.peak_wset >= ret.wset + assert ret.peak_paged_pool >= ret.paged_pool + assert ret.peak_nonpaged_pool >= ret.nonpaged_pool + assert ret.peak_pagefile >= ret.pagefile def memory_full_info(self, ret, info): assert is_namedtuple(ret) total = psutil.virtual_memory().total for name in ret._fields: value = getattr(ret, name) - self.assertIsInstance(value, (int, long)) - self.assertGreaterEqual(value, 0, msg=(name, value)) - if LINUX or OSX and name in ('vms', 'data'): + assert isinstance(value, (int, long)) + assert value >= 0 + if LINUX or (OSX and name in ('vms', 'data')): # On Linux there are processes (e.g. 'goa-daemon') whose # VMS is incredibly high for some reason. continue - self.assertLessEqual(value, total, msg=(name, value, total)) + assert value <= total, name if LINUX: - self.assertGreaterEqual(ret.pss, ret.uss) + assert ret.pss >= ret.uss def open_files(self, ret, info): - self.assertIsInstance(ret, list) + assert isinstance(ret, list) for f in ret: - self.assertIsInstance(f.fd, int) - self.assertIsInstance(f.path, str) - self.assertEqual(f.path.strip(), f.path) + assert isinstance(f.fd, int) + assert isinstance(f.path, str) + assert f.path.strip() == f.path if WINDOWS: - self.assertEqual(f.fd, -1) + assert f.fd == -1 elif LINUX: - self.assertIsInstance(f.position, int) - self.assertIsInstance(f.mode, str) - self.assertIsInstance(f.flags, int) - self.assertGreaterEqual(f.position, 0) - self.assertIn(f.mode, ('r', 'w', 'a', 'r+', 'a+')) - self.assertGreater(f.flags, 0) + assert isinstance(f.position, int) + assert isinstance(f.mode, str) + assert isinstance(f.flags, int) + assert f.position >= 0 + assert f.mode in ('r', 'w', 'a', 'r+', 'a+') + assert f.flags > 0 elif BSD and not f.path: # XXX see: https://github.com/giampaolo/psutil/issues/595 continue @@ -357,19 +357,19 @@ def open_files(self, ret, info): assert stat.S_ISREG(st.st_mode), f def num_fds(self, ret, info): - self.assertIsInstance(ret, int) - self.assertGreaterEqual(ret, 0) + assert isinstance(ret, int) + assert ret >= 0 def net_connections(self, ret, info): with create_sockets(): - self.assertEqual(len(ret), len(set(ret))) + assert len(ret) == len(set(ret)) for conn in ret: assert is_namedtuple(conn) check_connection_ntuple(conn) def cwd(self, ret, info): - self.assertIsInstance(ret, (str, unicode)) - self.assertEqual(ret.strip(), ret) + assert isinstance(ret, (str, unicode)) + assert ret.strip() == ret if ret: assert os.path.isabs(ret), ret try: @@ -384,31 +384,31 @@ def cwd(self, ret, info): assert stat.S_ISDIR(st.st_mode) def memory_percent(self, ret, info): - self.assertIsInstance(ret, float) + assert isinstance(ret, float) assert 0 <= ret <= 100, ret def is_running(self, ret, info): - self.assertIsInstance(ret, bool) + assert isinstance(ret, bool) def cpu_affinity(self, ret, info): - self.assertIsInstance(ret, list) - self.assertNotEqual(ret, []) + assert isinstance(ret, list) + assert ret != [] cpus = list(range(psutil.cpu_count())) for n in ret: - self.assertIsInstance(n, int) - self.assertIn(n, cpus) + assert isinstance(n, int) + assert n in cpus def terminal(self, ret, info): - self.assertIsInstance(ret, (str, type(None))) + assert isinstance(ret, (str, type(None))) if ret is not None: assert os.path.isabs(ret), ret assert os.path.exists(ret), ret def memory_maps(self, ret, info): for nt in ret: - self.assertIsInstance(nt.addr, str) - self.assertIsInstance(nt.perms, str) - self.assertIsInstance(nt.path, str) + assert isinstance(nt.addr, str) + assert isinstance(nt.perms, str) + assert isinstance(nt.path, str) for fname in nt._fields: value = getattr(nt, fname) if fname == 'path': @@ -423,15 +423,15 @@ def memory_maps(self, ret, info): if not WINDOWS: assert value, repr(value) else: - self.assertIsInstance(value, (int, long)) - self.assertGreaterEqual(value, 0) + assert isinstance(value, (int, long)) + assert value >= 0 def num_handles(self, ret, info): - self.assertIsInstance(ret, int) - self.assertGreaterEqual(ret, 0) + assert isinstance(ret, int) + assert ret >= 0 def nice(self, ret, info): - self.assertIsInstance(ret, int) + assert isinstance(ret, int) if POSIX: assert -20 <= ret <= 20, ret else: @@ -440,29 +440,29 @@ def nice(self, ret, info): for x in dir(psutil) if x.endswith('_PRIORITY_CLASS') ] - self.assertIn(ret, priorities) + assert ret in priorities if PY3: - self.assertIsInstance(ret, enum.IntEnum) + assert isinstance(ret, enum.IntEnum) else: - self.assertIsInstance(ret, int) + assert isinstance(ret, int) def num_ctx_switches(self, ret, info): assert is_namedtuple(ret) for value in ret: - self.assertIsInstance(value, (int, long)) - self.assertGreaterEqual(value, 0) + assert isinstance(value, (int, long)) + assert value >= 0 def rlimit(self, ret, info): - self.assertIsInstance(ret, tuple) - self.assertEqual(len(ret), 2) - self.assertGreaterEqual(ret[0], -1) - self.assertGreaterEqual(ret[1], -1) + assert isinstance(ret, tuple) + assert len(ret) == 2 + assert ret[0] >= -1 + assert ret[1] >= -1 def environ(self, ret, info): - self.assertIsInstance(ret, dict) + assert isinstance(ret, dict) for k, v in ret.items(): - self.assertIsInstance(k, str) - self.assertIsInstance(v, str) + assert isinstance(k, str) + assert isinstance(v, str) class TestPidsRange(PsutilTestCase): @@ -516,16 +516,16 @@ def check(pid): if exists: psutil.Process(pid) if not WINDOWS: # see docstring - self.assertIn(pid, psutil.pids()) + assert pid in psutil.pids() else: # On OpenBSD thread IDs can be instantiated, # and oneshot() succeeds, but other APIs fail # with EINVAL. if not OPENBSD: - with self.assertRaises(psutil.NoSuchProcess): + with pytest.raises(psutil.NoSuchProcess): psutil.Process(pid) if not WINDOWS: # see docstring - self.assertNotIn(pid, psutil.pids()) + assert pid not in psutil.pids() except (psutil.Error, AssertionError): x -= 1 if x == 0: @@ -541,9 +541,3 @@ def check(pid): continue with self.subTest(pid=pid): check(pid) - - -if __name__ == '__main__': - from psutil.tests.runner import run_from_name - - run_from_name(__file__) diff --git a/psutil/tests/test_sunos.py b/psutil/tests/test_sunos.py index 548352281..b9638ec44 100755 --- a/psutil/tests/test_sunos.py +++ b/psutil/tests/test_sunos.py @@ -7,15 +7,15 @@ """Sun OS specific tests.""" import os -import unittest import psutil from psutil import SUNOS from psutil.tests import PsutilTestCase +from psutil.tests import pytest from psutil.tests import sh -@unittest.skipIf(not SUNOS, "SUNOS only") +@pytest.mark.skipif(not SUNOS, reason="SUNOS only") class SunOSSpecificTestCase(PsutilTestCase): def test_swap_memory(self): out = sh('env PATH=/usr/sbin:/sbin:%s swap -l' % os.environ['PATH']) @@ -30,16 +30,10 @@ def test_swap_memory(self): used = total - free psutil_swap = psutil.swap_memory() - self.assertEqual(psutil_swap.total, total) - self.assertEqual(psutil_swap.used, used) - self.assertEqual(psutil_swap.free, free) + assert psutil_swap.total == total + assert psutil_swap.used == used + assert psutil_swap.free == free def test_cpu_count(self): out = sh("/usr/sbin/psrinfo") - self.assertEqual(psutil.cpu_count(), len(out.split('\n'))) - - -if __name__ == '__main__': - from psutil.tests.runner import run_from_name - - run_from_name(__file__) + assert psutil.cpu_count() == len(out.split('\n')) diff --git a/psutil/tests/test_system.py b/psutil/tests/test_system.py index e228f6d32..7d6695408 100755 --- a/psutil/tests/test_system.py +++ b/psutil/tests/test_system.py @@ -17,7 +17,6 @@ import socket import sys import time -import unittest import psutil from psutil import AIX @@ -54,6 +53,7 @@ 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 @@ -64,19 +64,18 @@ class TestProcessIter(PsutilTestCase): def test_pid_presence(self): - self.assertIn(os.getpid(), [x.pid for x in psutil.process_iter()]) + assert os.getpid() in [x.pid for x in psutil.process_iter()] sproc = self.spawn_testproc() - self.assertIn(sproc.pid, [x.pid for x in psutil.process_iter()]) + assert sproc.pid in [x.pid for x in psutil.process_iter()] p = psutil.Process(sproc.pid) p.kill() p.wait() - self.assertNotIn(sproc.pid, [x.pid for x in psutil.process_iter()]) + assert sproc.pid not in [x.pid for x in psutil.process_iter()] def test_no_duplicates(self): ls = [x for x in psutil.process_iter()] - self.assertEqual( - sorted(ls, key=lambda x: x.pid), - sorted(set(ls), key=lambda x: x.pid), + assert sorted(ls, key=lambda x: x.pid) == sorted( + set(ls), key=lambda x: x.pid ) def test_emulate_nsp(self): @@ -86,9 +85,7 @@ def test_emulate_nsp(self): 'psutil.Process.as_dict', side_effect=psutil.NoSuchProcess(os.getpid()), ): - self.assertEqual( - list(psutil.process_iter(attrs=["cpu_times"])), [] - ) + assert list(psutil.process_iter(attrs=["cpu_times"])) == [] psutil.process_iter.cache_clear() # repeat test without cache def test_emulate_access_denied(self): @@ -98,25 +95,25 @@ def test_emulate_access_denied(self): 'psutil.Process.as_dict', side_effect=psutil.AccessDenied(os.getpid()), ): - with self.assertRaises(psutil.AccessDenied): + with pytest.raises(psutil.AccessDenied): list(psutil.process_iter(attrs=["cpu_times"])) psutil.process_iter.cache_clear() # repeat test without cache def test_attrs(self): for p in psutil.process_iter(attrs=['pid']): - self.assertEqual(list(p.info.keys()), ['pid']) + assert list(p.info.keys()) == ['pid'] # yield again for p in psutil.process_iter(attrs=['pid']): - self.assertEqual(list(p.info.keys()), ['pid']) - with self.assertRaises(ValueError): + assert list(p.info.keys()) == ['pid'] + with pytest.raises(ValueError): list(psutil.process_iter(attrs=['foo'])) with mock.patch( "psutil._psplatform.Process.cpu_times", side_effect=psutil.AccessDenied(0, ""), ) as m: for p in psutil.process_iter(attrs=["pid", "cpu_times"]): - self.assertIsNone(p.info['cpu_times']) - self.assertGreaterEqual(p.info['pid'], 0) + assert p.info['cpu_times'] is None + assert p.info['pid'] >= 0 assert m.called with mock.patch( "psutil._psplatform.Process.cpu_times", @@ -126,8 +123,8 @@ def test_attrs(self): for p in psutil.process_iter( attrs=["pid", "cpu_times"], ad_value=flag ): - self.assertIs(p.info['cpu_times'], flag) - self.assertGreaterEqual(p.info['pid'], 0) + assert p.info['cpu_times'] is flag + assert p.info['pid'] >= 0 assert m.called def test_cache_clear(self): @@ -138,8 +135,9 @@ def test_cache_clear(self): class TestProcessAPIs(PsutilTestCase): - @unittest.skipIf( - PYPY and WINDOWS, "spawn_testproc() unreliable on PYPY + WINDOWS" + @pytest.mark.skipif( + PYPY and WINDOWS, + reason="spawn_testproc() unreliable on PYPY + WINDOWS", ) def test_wait_procs(self): def callback(p): @@ -150,56 +148,59 @@ def callback(p): sproc2 = self.spawn_testproc() sproc3 = self.spawn_testproc() procs = [psutil.Process(x.pid) for x in (sproc1, sproc2, sproc3)] - self.assertRaises(ValueError, psutil.wait_procs, procs, timeout=-1) - self.assertRaises(TypeError, psutil.wait_procs, procs, callback=1) + with pytest.raises(ValueError): + psutil.wait_procs(procs, timeout=-1) + with pytest.raises(TypeError): + psutil.wait_procs(procs, callback=1) t = time.time() gone, alive = psutil.wait_procs(procs, timeout=0.01, callback=callback) - self.assertLess(time.time() - t, 0.5) - self.assertEqual(gone, []) - self.assertEqual(len(alive), 3) - self.assertEqual(pids, []) + assert time.time() - t < 0.5 + assert gone == [] + assert len(alive) == 3 + assert pids == [] for p in alive: - self.assertFalse(hasattr(p, 'returncode')) + assert not hasattr(p, 'returncode') @retry_on_failure(30) def test_1(procs, callback): gone, alive = psutil.wait_procs( procs, timeout=0.03, callback=callback ) - self.assertEqual(len(gone), 1) - self.assertEqual(len(alive), 2) + assert len(gone) == 1 + assert len(alive) == 2 return gone, alive sproc3.terminate() gone, alive = test_1(procs, callback) - self.assertIn(sproc3.pid, [x.pid for x in gone]) + assert sproc3.pid in [x.pid for x in gone] if POSIX: - self.assertEqual(gone.pop().returncode, -signal.SIGTERM) + assert gone.pop().returncode == -signal.SIGTERM else: - self.assertEqual(gone.pop().returncode, 1) - self.assertEqual(pids, [sproc3.pid]) + assert gone.pop().returncode == 1 + assert pids == [sproc3.pid] for p in alive: - self.assertFalse(hasattr(p, 'returncode')) + assert not hasattr(p, 'returncode') @retry_on_failure(30) def test_2(procs, callback): gone, alive = psutil.wait_procs( procs, timeout=0.03, callback=callback ) - self.assertEqual(len(gone), 3) - self.assertEqual(len(alive), 0) + assert len(gone) == 3 + assert len(alive) == 0 return gone, alive sproc1.terminate() sproc2.terminate() gone, alive = test_2(procs, callback) - self.assertEqual(set(pids), set([sproc1.pid, sproc2.pid, sproc3.pid])) + assert set(pids) == set([sproc1.pid, sproc2.pid, sproc3.pid]) for p in gone: - self.assertTrue(hasattr(p, 'returncode')) + assert hasattr(p, 'returncode') - @unittest.skipIf( - PYPY and WINDOWS, "spawn_testproc() unreliable on PYPY + WINDOWS" + @pytest.mark.skipif( + PYPY and WINDOWS, + reason="spawn_testproc() unreliable on PYPY + WINDOWS", ) def test_wait_procs_no_timeout(self): sproc1 = self.spawn_testproc() @@ -212,13 +213,13 @@ def test_wait_procs_no_timeout(self): def test_pid_exists(self): sproc = self.spawn_testproc() - self.assertTrue(psutil.pid_exists(sproc.pid)) + assert psutil.pid_exists(sproc.pid) p = psutil.Process(sproc.pid) p.kill() p.wait() - self.assertFalse(psutil.pid_exists(sproc.pid)) - self.assertFalse(psutil.pid_exists(-1)) - self.assertEqual(psutil.pid_exists(0), 0 in psutil.pids()) + assert not psutil.pid_exists(sproc.pid) + assert not psutil.pid_exists(-1) + assert psutil.pid_exists(0) == (0 in psutil.pids()) def test_pid_exists_2(self): pids = psutil.pids() @@ -229,36 +230,38 @@ def test_pid_exists_2(self): # in case the process disappeared in meantime fail only # if it is no longer in psutil.pids() time.sleep(0.1) - self.assertNotIn(pid, psutil.pids()) + assert pid not in psutil.pids() pids = range(max(pids) + 15000, max(pids) + 16000) for pid in pids: - self.assertFalse(psutil.pid_exists(pid), msg=pid) + assert not psutil.pid_exists(pid) class TestMiscAPIs(PsutilTestCase): def test_boot_time(self): bt = psutil.boot_time() - self.assertIsInstance(bt, float) - self.assertGreater(bt, 0) - self.assertLess(bt, time.time()) + assert isinstance(bt, float) + assert bt > 0 + assert bt < time.time() - @unittest.skipIf(CI_TESTING and not psutil.users(), "unreliable on CI") + @pytest.mark.skipif( + CI_TESTING and not psutil.users(), reason="unreliable on CI" + ) def test_users(self): users = psutil.users() - self.assertNotEqual(users, []) + assert users != [] for user in users: with self.subTest(user=user): assert user.name - self.assertIsInstance(user.name, str) - self.assertIsInstance(user.terminal, (str, type(None))) + assert isinstance(user.name, str) + assert isinstance(user.terminal, (str, type(None))) if user.host is not None: - self.assertIsInstance(user.host, (str, type(None))) + assert isinstance(user.host, (str, type(None))) user.terminal # noqa user.host # noqa - self.assertGreater(user.started, 0.0) + assert user.started > 0.0 datetime.datetime.fromtimestamp(user.started) if WINDOWS or OPENBSD: - self.assertIsNone(user.pid) + assert user.pid is None else: psutil.Process(user.pid) @@ -284,7 +287,7 @@ def test_os_constants(self): "SUNOS", ] for name in names: - self.assertIsInstance(getattr(psutil, name), bool, msg=name) + assert isinstance(getattr(psutil, name), bool), name if os.name == 'posix': assert psutil.POSIX @@ -295,12 +298,9 @@ def test_os_constants(self): names.remove("LINUX") elif "bsd" in sys.platform.lower(): assert psutil.BSD - self.assertEqual( - [psutil.FREEBSD, psutil.OPENBSD, psutil.NETBSD].count( - True - ), - 1, - ) + assert [psutil.FREEBSD, psutil.OPENBSD, psutil.NETBSD].count( + True + ) == 1 names.remove("BSD") names.remove("FREEBSD") names.remove("OPENBSD") @@ -321,7 +321,7 @@ def test_os_constants(self): # assert all other constants are set to False for name in names: - self.assertFalse(getattr(psutil, name), msg=name) + assert not getattr(psutil, name), name class TestMemoryAPIs(PsutilTestCase): @@ -335,7 +335,7 @@ def test_virtual_memory(self): for name in mem._fields: value = getattr(mem, name) if name != 'percent': - self.assertIsInstance(value, (int, long)) + assert isinstance(value, (int, long)) if name != 'total': if not value >= 0: raise self.fail("%r < 0 (%s)" % (name, value)) @@ -347,8 +347,13 @@ def test_virtual_memory(self): def test_swap_memory(self): mem = psutil.swap_memory() - self.assertEqual( - mem._fields, ('total', 'used', 'free', 'percent', 'sin', 'sout') + assert mem._fields == ( + 'total', + 'used', + 'free', + 'percent', + 'sin', + 'sout', ) assert mem.total >= 0, mem @@ -366,26 +371,26 @@ def test_swap_memory(self): class TestCpuAPIs(PsutilTestCase): def test_cpu_count_logical(self): logical = psutil.cpu_count() - self.assertIsNotNone(logical) - self.assertEqual(logical, len(psutil.cpu_times(percpu=True))) - self.assertGreaterEqual(logical, 1) + assert logical is not None + assert logical == len(psutil.cpu_times(percpu=True)) + assert logical >= 1 if os.path.exists("/proc/cpuinfo"): with open("/proc/cpuinfo") as fd: cpuinfo_data = fd.read() if "physical id" not in cpuinfo_data: - raise unittest.SkipTest("cpuinfo doesn't include physical id") + raise pytest.skip("cpuinfo doesn't include physical id") def test_cpu_count_cores(self): logical = psutil.cpu_count() cores = psutil.cpu_count(logical=False) if cores is None: - raise unittest.SkipTest("cpu_count_cores() is None") + raise pytest.skip("cpu_count_cores() is None") if WINDOWS and sys.getwindowsversion()[:2] <= (6, 1): # <= Vista - self.assertIsNone(cores) + assert cores is None else: - self.assertGreaterEqual(cores, 1) - self.assertGreaterEqual(logical, cores) + assert cores >= 1 + assert logical >= cores def test_cpu_count_none(self): # https://github.com/giampaolo/psutil/issues/1085 @@ -393,12 +398,12 @@ def test_cpu_count_none(self): with mock.patch( 'psutil._psplatform.cpu_count_logical', return_value=val ) as m: - self.assertIsNone(psutil.cpu_count()) + assert psutil.cpu_count() is None assert m.called with mock.patch( 'psutil._psplatform.cpu_count_cores', return_value=val ) as m: - self.assertIsNone(psutil.cpu_count(logical=False)) + assert psutil.cpu_count(logical=False) is None assert m.called def test_cpu_times(self): @@ -407,10 +412,10 @@ def test_cpu_times(self): times = psutil.cpu_times() sum(times) for cp_time in times: - self.assertIsInstance(cp_time, float) - self.assertGreaterEqual(cp_time, 0.0) + assert isinstance(cp_time, float) + assert cp_time >= 0.0 total += cp_time - self.assertAlmostEqual(total, sum(times), places=6) + assert round(abs(total - sum(times)), 6) == 0 str(times) # CPU times are always supposed to increase over time # or at least remain the same and that's because time @@ -446,14 +451,13 @@ def test_per_cpu_times(self): total = 0 sum(times) for cp_time in times: - self.assertIsInstance(cp_time, float) - self.assertGreaterEqual(cp_time, 0.0) + assert isinstance(cp_time, float) + assert cp_time >= 0.0 total += cp_time - self.assertAlmostEqual(total, sum(times), places=6) + assert round(abs(total - sum(times)), 6) == 0 str(times) - self.assertEqual( - len(psutil.cpu_times(percpu=True)[0]), - len(psutil.cpu_times(percpu=False)), + assert len(psutil.cpu_times(percpu=True)[0]) == len( + psutil.cpu_times(percpu=False) ) # Note: in theory CPU times are always supposed to increase over @@ -490,7 +494,9 @@ def test_per_cpu_times_2(self): if difference >= 0.05: return - @unittest.skipIf(CI_TESTING and OPENBSD, "unreliable on OPENBSD + CI") + @pytest.mark.skipif( + CI_TESTING and OPENBSD, reason="unreliable on OPENBSD + CI" + ) def test_cpu_times_comparison(self): # Make sure the sum of all per cpu times is almost equal to # base "one cpu" times. On OpenBSD the sum of per-CPUs is @@ -500,18 +506,17 @@ def test_cpu_times_comparison(self): summed_values = base._make([sum(num) for num in zip(*per_cpu)]) for field in base._fields: with self.subTest(field=field, base=base, per_cpu=per_cpu): - self.assertAlmostEqual( - getattr(base, field), - getattr(summed_values, field), - delta=1, + assert ( + abs(getattr(base, field) - getattr(summed_values, field)) + < 1 ) def _test_cpu_percent(self, percent, last_ret, new_ret): try: - self.assertIsInstance(percent, float) - self.assertGreaterEqual(percent, 0.0) - self.assertIsNot(percent, -0.0) - self.assertLessEqual(percent, 100.0 * psutil.cpu_count()) + assert isinstance(percent, float) + assert percent >= 0.0 + assert percent is not -0.0 + assert percent <= 100.0 * psutil.cpu_count() except AssertionError as err: raise AssertionError( "\n%s\nlast=%s\nnew=%s" @@ -524,18 +529,18 @@ def test_cpu_percent(self): new = psutil.cpu_percent(interval=None) self._test_cpu_percent(new, last, new) last = new - with self.assertRaises(ValueError): + with pytest.raises(ValueError): psutil.cpu_percent(interval=-1) def test_per_cpu_percent(self): last = psutil.cpu_percent(interval=0.001, percpu=True) - self.assertEqual(len(last), psutil.cpu_count()) + assert len(last) == psutil.cpu_count() for _ in range(100): new = psutil.cpu_percent(interval=None, percpu=True) for percent in new: self._test_cpu_percent(percent, last, new) last = new - with self.assertRaises(ValueError): + with pytest.raises(ValueError): psutil.cpu_percent(interval=-1, percpu=True) def test_cpu_times_percent(self): @@ -546,12 +551,12 @@ def test_cpu_times_percent(self): self._test_cpu_percent(percent, last, new) self._test_cpu_percent(sum(new), last, new) last = new - with self.assertRaises(ValueError): + with pytest.raises(ValueError): psutil.cpu_times_percent(interval=-1) def test_per_cpu_times_percent(self): last = psutil.cpu_times_percent(interval=0.001, percpu=True) - self.assertEqual(len(last), psutil.cpu_count()) + assert len(last) == psutil.cpu_count() for _ in range(100): new = psutil.cpu_times_percent(interval=None, percpu=True) for cpu in new: @@ -575,57 +580,61 @@ def test_per_cpu_times_percent_negative(self): def test_cpu_stats(self): # Tested more extensively in per-platform test modules. infos = psutil.cpu_stats() - self.assertEqual( - infos._fields, - ('ctx_switches', 'interrupts', 'soft_interrupts', 'syscalls'), + assert infos._fields == ( + 'ctx_switches', + 'interrupts', + 'soft_interrupts', + 'syscalls', ) for name in infos._fields: value = getattr(infos, name) - self.assertGreaterEqual(value, 0) + assert value >= 0 # on AIX, ctx_switches is always 0 if not AIX and name in ('ctx_switches', 'interrupts'): - self.assertGreater(value, 0) + assert value > 0 # TODO: remove this once 1892 is fixed - @unittest.skipIf( - MACOS and platform.machine() == 'arm64', "skipped due to #1892" + @pytest.mark.skipif( + MACOS and platform.machine() == 'arm64', reason="skipped due to #1892" ) - @unittest.skipIf(not HAS_CPU_FREQ, "not supported") + @pytest.mark.skipif(not HAS_CPU_FREQ, reason="not supported") def test_cpu_freq(self): def check_ls(ls): for nt in ls: - self.assertEqual(nt._fields, ('current', 'min', 'max')) + assert nt._fields == ('current', 'min', 'max') if nt.max != 0.0: - self.assertLessEqual(nt.current, nt.max) + assert nt.current <= nt.max for name in nt._fields: value = getattr(nt, name) - self.assertIsInstance(value, (int, long, float)) - self.assertGreaterEqual(value, 0) + assert isinstance(value, (int, long, float)) + assert value >= 0 ls = psutil.cpu_freq(percpu=True) if FREEBSD and not ls: - raise unittest.SkipTest("returns empty list on FreeBSD") + raise pytest.skip("returns empty list on FreeBSD") assert ls, ls check_ls([psutil.cpu_freq(percpu=False)]) if LINUX: - self.assertEqual(len(ls), psutil.cpu_count()) + assert len(ls) == psutil.cpu_count() - @unittest.skipIf(not HAS_GETLOADAVG, "not supported") + @pytest.mark.skipif(not HAS_GETLOADAVG, reason="not supported") def test_getloadavg(self): loadavg = psutil.getloadavg() - self.assertEqual(len(loadavg), 3) + assert len(loadavg) == 3 for load in loadavg: - self.assertIsInstance(load, float) - self.assertGreaterEqual(load, 0.0) + assert isinstance(load, float) + assert load >= 0.0 class TestDiskAPIs(PsutilTestCase): - @unittest.skipIf(PYPY and not IS_64BIT, "unreliable on PYPY32 + 32BIT") + @pytest.mark.skipif( + PYPY and not IS_64BIT, reason="unreliable on PYPY32 + 32BIT" + ) def test_disk_usage(self): usage = psutil.disk_usage(os.getcwd()) - self.assertEqual(usage._fields, ('total', 'used', 'free', 'percent')) + assert usage._fields == ('total', 'used', 'free', 'percent') assert usage.total > 0, usage assert usage.used > 0, usage @@ -637,26 +646,22 @@ def test_disk_usage(self): # py >= 3.3, see: http://bugs.python.org/issue12442 shutil_usage = shutil.disk_usage(os.getcwd()) tolerance = 5 * 1024 * 1024 # 5MB - self.assertEqual(usage.total, shutil_usage.total) - self.assertAlmostEqual( - usage.free, shutil_usage.free, delta=tolerance - ) + assert usage.total == shutil_usage.total + assert abs(usage.free - shutil_usage.free) < tolerance if not MACOS_12PLUS: # see https://github.com/giampaolo/psutil/issues/2147 - self.assertAlmostEqual( - usage.used, shutil_usage.used, delta=tolerance - ) + assert abs(usage.used - shutil_usage.used) < tolerance # if path does not exist OSError ENOENT is expected across # all platforms fname = self.get_testfn() - with self.assertRaises(FileNotFoundError): + with pytest.raises(FileNotFoundError): psutil.disk_usage(fname) - @unittest.skipIf(not ASCII_FS, "not an ASCII fs") + @pytest.mark.skipif(not ASCII_FS, reason="not an ASCII fs") def test_disk_usage_unicode(self): # See: https://github.com/giampaolo/psutil/issues/416 - with self.assertRaises(UnicodeEncodeError): + with pytest.raises(UnicodeEncodeError): psutil.disk_usage(UNICODE_SUFFIX) def test_disk_usage_bytes(self): @@ -664,14 +669,14 @@ def test_disk_usage_bytes(self): def test_disk_partitions(self): def check_ntuple(nt): - self.assertIsInstance(nt.device, str) - self.assertIsInstance(nt.mountpoint, str) - self.assertIsInstance(nt.fstype, str) - self.assertIsInstance(nt.opts, str) + assert isinstance(nt.device, str) + assert isinstance(nt.mountpoint, str) + assert isinstance(nt.fstype, str) + assert isinstance(nt.opts, str) # all = False ls = psutil.disk_partitions(all=False) - self.assertTrue(ls, msg=ls) + assert ls for disk in ls: check_ntuple(disk) if WINDOWS and 'cdrom' in disk.opts: @@ -688,7 +693,7 @@ def check_ntuple(nt): # all = True ls = psutil.disk_partitions(all=True) - self.assertTrue(ls, msg=ls) + assert ls for disk in psutil.disk_partitions(all=True): check_ntuple(disk) if not WINDOWS and disk.mountpoint: @@ -718,30 +723,30 @@ def find_mount_point(path): for x in psutil.disk_partitions(all=True) if x.mountpoint ] - self.assertIn(mount, mounts) + assert mount in mounts - @unittest.skipIf( + @pytest.mark.skipif( LINUX and not os.path.exists('/proc/diskstats'), - '/proc/diskstats not available on this linux version', + reason="/proc/diskstats not available on this linux version", ) - @unittest.skipIf( - CI_TESTING and not psutil.disk_io_counters(), "unreliable on CI" + @pytest.mark.skipif( + CI_TESTING and not psutil.disk_io_counters(), reason="unreliable on CI" ) # no visible disks def test_disk_io_counters(self): def check_ntuple(nt): - self.assertEqual(nt[0], nt.read_count) - self.assertEqual(nt[1], nt.write_count) - self.assertEqual(nt[2], nt.read_bytes) - self.assertEqual(nt[3], nt.write_bytes) + assert nt[0] == nt.read_count + assert nt[1] == nt.write_count + assert nt[2] == nt.read_bytes + assert nt[3] == nt.write_bytes if not (OPENBSD or NETBSD): - self.assertEqual(nt[4], nt.read_time) - self.assertEqual(nt[5], nt.write_time) + assert nt[4] == nt.read_time + assert nt[5] == nt.write_time if LINUX: - self.assertEqual(nt[6], nt.read_merged_count) - self.assertEqual(nt[7], nt.write_merged_count) - self.assertEqual(nt[8], nt.busy_time) + assert nt[6] == nt.read_merged_count + assert nt[7] == nt.write_merged_count + assert nt[8] == nt.busy_time elif FREEBSD: - self.assertEqual(nt[6], nt.busy_time) + assert nt[6] == nt.busy_time for name in nt._fields: assert getattr(nt, name) >= 0, nt @@ -750,7 +755,7 @@ def check_ntuple(nt): check_ntuple(ret) ret = psutil.disk_io_counters(perdisk=True) # make sure there are no duplicates - self.assertEqual(len(ret), len(set(ret))) + assert len(ret) == len(set(ret)) for key in ret: assert key, key check_ntuple(ret[key]) @@ -761,23 +766,23 @@ def test_disk_io_counters_no_disks(self): with mock.patch( 'psutil._psplatform.disk_io_counters', return_value={} ) as m: - self.assertIsNone(psutil.disk_io_counters(perdisk=False)) - self.assertEqual(psutil.disk_io_counters(perdisk=True), {}) + assert psutil.disk_io_counters(perdisk=False) is None + assert psutil.disk_io_counters(perdisk=True) == {} assert m.called class TestNetAPIs(PsutilTestCase): - @unittest.skipIf(not HAS_NET_IO_COUNTERS, 'not supported') + @pytest.mark.skipif(not HAS_NET_IO_COUNTERS, reason="not supported") def test_net_io_counters(self): def check_ntuple(nt): - self.assertEqual(nt[0], nt.bytes_sent) - self.assertEqual(nt[1], nt.bytes_recv) - self.assertEqual(nt[2], nt.packets_sent) - self.assertEqual(nt[3], nt.packets_recv) - self.assertEqual(nt[4], nt.errin) - self.assertEqual(nt[5], nt.errout) - self.assertEqual(nt[6], nt.dropin) - self.assertEqual(nt[7], nt.dropout) + assert nt[0] == nt.bytes_sent + assert nt[1] == nt.bytes_recv + assert nt[2] == nt.packets_sent + assert nt[3] == nt.packets_recv + assert nt[4] == nt.errin + assert nt[5] == nt.errout + assert nt[6] == nt.dropin + assert nt[7] == nt.dropout assert nt.bytes_sent >= 0, nt assert nt.bytes_recv >= 0, nt assert nt.packets_sent >= 0, nt @@ -790,24 +795,24 @@ def check_ntuple(nt): ret = psutil.net_io_counters(pernic=False) check_ntuple(ret) ret = psutil.net_io_counters(pernic=True) - self.assertNotEqual(ret, []) + assert ret != [] for key in ret: - self.assertTrue(key) - self.assertIsInstance(key, str) + assert key + assert isinstance(key, str) check_ntuple(ret[key]) - @unittest.skipIf(not HAS_NET_IO_COUNTERS, 'not supported') + @pytest.mark.skipif(not HAS_NET_IO_COUNTERS, reason="not supported") def test_net_io_counters_no_nics(self): # Emulate a case where no NICs are installed, see: # https://github.com/giampaolo/psutil/issues/1062 with mock.patch( 'psutil._psplatform.net_io_counters', return_value={} ) as m: - self.assertIsNone(psutil.net_io_counters(pernic=False)) - self.assertEqual(psutil.net_io_counters(pernic=True), {}) + assert psutil.net_io_counters(pernic=False) is None + assert psutil.net_io_counters(pernic=True) == {} assert m.called - @unittest.skipIf(QEMU_USER, 'QEMU user not supported') + @pytest.mark.skipif(QEMU_USER, reason="QEMU user not supported") def test_net_if_addrs(self): nics = psutil.net_if_addrs() assert nics, nics @@ -821,16 +826,16 @@ def test_net_if_addrs(self): families = set([socket.AF_INET, socket.AF_INET6, psutil.AF_LINK]) for nic, addrs in nics.items(): - self.assertIsInstance(nic, str) - self.assertEqual(len(set(addrs)), len(addrs)) + assert isinstance(nic, str) + assert len(set(addrs)) == len(addrs) for addr in addrs: - self.assertIsInstance(addr.family, int) - self.assertIsInstance(addr.address, str) - self.assertIsInstance(addr.netmask, (str, type(None))) - self.assertIsInstance(addr.broadcast, (str, type(None))) - self.assertIn(addr.family, families) + assert isinstance(addr.family, int) + assert isinstance(addr.address, str) + assert isinstance(addr.netmask, (str, type(None))) + assert isinstance(addr.broadcast, (str, type(None))) + assert addr.family in families if PY3 and not PYPY: - self.assertIsInstance(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 @@ -865,17 +870,17 @@ def test_net_if_addrs(self): check_net_address(ip, addr.family) # broadcast and ptp addresses are mutually exclusive if addr.broadcast: - self.assertIsNone(addr.ptp) + assert addr.ptp is None elif addr.ptp: - self.assertIsNone(addr.broadcast) + assert addr.broadcast is None if BSD or MACOS or SUNOS: if hasattr(socket, "AF_LINK"): - self.assertEqual(psutil.AF_LINK, socket.AF_LINK) + assert psutil.AF_LINK == socket.AF_LINK elif LINUX: - self.assertEqual(psutil.AF_LINK, socket.AF_PACKET) + assert psutil.AF_LINK == socket.AF_PACKET elif WINDOWS: - self.assertEqual(psutil.AF_LINK, -1) + assert psutil.AF_LINK == -1 def test_net_if_addrs_mac_null_bytes(self): # Simulate that the underlying C function returns an incomplete @@ -891,11 +896,11 @@ def test_net_if_addrs_mac_null_bytes(self): addr = psutil.net_if_addrs()['em1'][0] assert m.called if POSIX: - self.assertEqual(addr.address, '06:3d:29:00:00:00') + assert addr.address == '06:3d:29:00:00:00' else: - self.assertEqual(addr.address, '06-3d-29-00-00-00') + assert addr.address == '06-3d-29-00-00-00' - @unittest.skipIf(QEMU_USER, 'QEMU user not supported') + @pytest.mark.skipif(QEMU_USER, reason="QEMU user not supported") def test_net_if_stats(self): nics = psutil.net_if_stats() assert nics, nics @@ -905,17 +910,17 @@ def test_net_if_stats(self): psutil.NIC_DUPLEX_UNKNOWN, ) for name, stats in nics.items(): - self.assertIsInstance(name, str) + assert isinstance(name, str) isup, duplex, speed, mtu, flags = stats - self.assertIsInstance(isup, bool) - self.assertIn(duplex, all_duplexes) - self.assertIn(duplex, all_duplexes) - self.assertGreaterEqual(speed, 0) - self.assertGreaterEqual(mtu, 0) - self.assertIsInstance(flags, str) - - @unittest.skipIf( - not (LINUX or BSD or MACOS), "LINUX or BSD or MACOS specific" + assert isinstance(isup, bool) + assert duplex in all_duplexes + assert duplex in all_duplexes + assert speed >= 0 + assert mtu >= 0 + assert isinstance(flags, str) + + @pytest.mark.skipif( + not (LINUX or BSD or MACOS), reason="LINUX or BSD or MACOS specific" ) def test_net_if_stats_enodev(self): # See: https://github.com/giampaolo/psutil/issues/1279 @@ -924,26 +929,26 @@ def test_net_if_stats_enodev(self): side_effect=OSError(errno.ENODEV, ""), ) as m: ret = psutil.net_if_stats() - self.assertEqual(ret, {}) + assert ret == {} assert m.called class TestSensorsAPIs(PsutilTestCase): - @unittest.skipIf(not HAS_SENSORS_TEMPERATURES, "not supported") + @pytest.mark.skipif(not HAS_SENSORS_TEMPERATURES, reason="not supported") def test_sensors_temperatures(self): temps = psutil.sensors_temperatures() for name, entries in temps.items(): - self.assertIsInstance(name, str) + assert isinstance(name, str) for entry in entries: - self.assertIsInstance(entry.label, str) + assert isinstance(entry.label, str) if entry.current is not None: - self.assertGreaterEqual(entry.current, 0) + assert entry.current >= 0 if entry.high is not None: - self.assertGreaterEqual(entry.high, 0) + assert entry.high >= 0 if entry.critical is not None: - self.assertGreaterEqual(entry.critical, 0) + assert entry.critical >= 0 - @unittest.skipIf(not HAS_SENSORS_TEMPERATURES, "not supported") + @pytest.mark.skipif(not HAS_SENSORS_TEMPERATURES, reason="not supported") def test_sensors_temperatures_fahreneit(self): d = {'coretemp': [('label', 50.0, 60.0, 70.0)]} with mock.patch( @@ -951,38 +956,32 @@ def test_sensors_temperatures_fahreneit(self): ) as m: temps = psutil.sensors_temperatures(fahrenheit=True)['coretemp'][0] assert m.called - self.assertEqual(temps.current, 122.0) - self.assertEqual(temps.high, 140.0) - self.assertEqual(temps.critical, 158.0) + assert temps.current == 122.0 + assert temps.high == 140.0 + assert temps.critical == 158.0 - @unittest.skipIf(not HAS_SENSORS_BATTERY, "not supported") - @unittest.skipIf(not HAS_BATTERY, "no battery") + @pytest.mark.skipif(not HAS_SENSORS_BATTERY, reason="not supported") + @pytest.mark.skipif(not HAS_BATTERY, reason="no battery") def test_sensors_battery(self): ret = psutil.sensors_battery() - self.assertGreaterEqual(ret.percent, 0) - self.assertLessEqual(ret.percent, 100) + assert ret.percent >= 0 + assert ret.percent <= 100 if ret.secsleft not in ( psutil.POWER_TIME_UNKNOWN, psutil.POWER_TIME_UNLIMITED, ): - self.assertGreaterEqual(ret.secsleft, 0) + assert ret.secsleft >= 0 else: if ret.secsleft == psutil.POWER_TIME_UNLIMITED: - self.assertTrue(ret.power_plugged) - self.assertIsInstance(ret.power_plugged, bool) + assert ret.power_plugged + assert isinstance(ret.power_plugged, bool) - @unittest.skipIf(not HAS_SENSORS_FANS, "not supported") + @pytest.mark.skipif(not HAS_SENSORS_FANS, reason="not supported") def test_sensors_fans(self): fans = psutil.sensors_fans() for name, entries in fans.items(): - self.assertIsInstance(name, str) + assert isinstance(name, str) for entry in entries: - self.assertIsInstance(entry.label, str) - self.assertIsInstance(entry.current, (int, long)) - self.assertGreaterEqual(entry.current, 0) - - -if __name__ == '__main__': - from psutil.tests.runner import run_from_name - - run_from_name(__file__) + assert isinstance(entry.label, str) + assert isinstance(entry.current, (int, long)) + assert entry.current >= 0 diff --git a/psutil/tests/test_testutils.py b/psutil/tests/test_testutils.py index ef98a3abe..e11c8f783 100755 --- a/psutil/tests/test_testutils.py +++ b/psutil/tests/test_testutils.py @@ -14,7 +14,9 @@ import socket import stat import subprocess +import textwrap import unittest +import warnings import psutil import psutil.tests @@ -24,9 +26,11 @@ 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 +from psutil.tests import HERE from psutil.tests import PYTHON_EXE from psutil.tests import PYTHON_EXE_ENV from psutil.tests import PsutilTestCase @@ -36,17 +40,18 @@ from psutil.tests import call_until from psutil.tests import chdir from psutil.tests import create_sockets +from psutil.tests import fake_pytest from psutil.tests import filter_proc_net_connections from psutil.tests import get_free_port from psutil.tests import is_namedtuple from psutil.tests import mock from psutil.tests import process_namespace +from psutil.tests import pytest from psutil.tests import reap_children from psutil.tests import retry from psutil.tests import retry_on_failure from psutil.tests import safe_mkdir from psutil.tests import safe_rmpath -from psutil.tests import serialrun from psutil.tests import system_namespace from psutil.tests import tcp_socketpair from psutil.tests import terminate @@ -73,8 +78,8 @@ def foo(): return 1 queue = list(range(3)) - self.assertEqual(foo(), 1) - self.assertEqual(sleep.call_count, 3) + assert foo() == 1 + assert sleep.call_count == 3 @mock.patch('time.sleep') def test_retry_failure(self, sleep): @@ -87,8 +92,9 @@ def foo(): return 1 queue = list(range(6)) - self.assertRaises(ZeroDivisionError, foo) - self.assertEqual(sleep.call_count, 5) + with pytest.raises(ZeroDivisionError): + foo() + assert sleep.call_count == 5 @mock.patch('time.sleep') def test_exception_arg(self, sleep): @@ -96,8 +102,9 @@ def test_exception_arg(self, sleep): def foo(): raise TypeError - self.assertRaises(TypeError, foo) - self.assertEqual(sleep.call_count, 0) + with pytest.raises(TypeError): + foo() + assert sleep.call_count == 0 @mock.patch('time.sleep') def test_no_interval_arg(self, sleep): @@ -107,8 +114,9 @@ def test_no_interval_arg(self, sleep): def foo(): 1 / 0 # noqa - self.assertRaises(ZeroDivisionError, foo) - self.assertEqual(sleep.call_count, 0) + with pytest.raises(ZeroDivisionError): + foo() + assert sleep.call_count == 0 @mock.patch('time.sleep') def test_retries_arg(self, sleep): @@ -116,12 +124,14 @@ def test_retries_arg(self, sleep): def foo(): 1 / 0 # noqa - self.assertRaises(ZeroDivisionError, foo) - self.assertEqual(sleep.call_count, 5) + with pytest.raises(ZeroDivisionError): + foo() + assert sleep.call_count == 5 @mock.patch('time.sleep') def test_retries_and_timeout_args(self, sleep): - self.assertRaises(ValueError, retry, retries=5, timeout=1) + with pytest.raises(ValueError): + retry(retries=5, timeout=1) class TestSyncTestUtils(PsutilTestCase): @@ -129,7 +139,8 @@ def test_wait_for_pid(self): wait_for_pid(os.getpid()) nopid = max(psutil.pids()) + 99999 with mock.patch('psutil.tests.retry.__iter__', return_value=iter([0])): - self.assertRaises(psutil.NoSuchProcess, wait_for_pid, nopid) + with pytest.raises(psutil.NoSuchProcess): + wait_for_pid(nopid) def test_wait_for_file(self): testfn = self.get_testfn() @@ -148,7 +159,8 @@ 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])): - self.assertRaises(IOError, wait_for_file, testfn) + with pytest.raises(IOError): + wait_for_file(testfn) def test_wait_for_file_no_delete(self): testfn = self.get_testfn() @@ -158,18 +170,18 @@ def test_wait_for_file_no_delete(self): assert os.path.exists(testfn) def test_call_until(self): - ret = call_until(lambda: 1, "ret == 1") - self.assertEqual(ret, 1) + call_until(lambda: 1) + # TODO: test for timeout class TestFSTestUtils(PsutilTestCase): def test_open_text(self): with open_text(__file__) as f: - self.assertEqual(f.mode, 'r') + assert f.mode == 'r' def test_open_binary(self): with open_binary(__file__) as f: - self.assertEqual(f.mode, 'rb') + assert f.mode == 'rb' def test_safe_mkdir(self): testfn = self.get_testfn() @@ -194,7 +206,7 @@ def test_safe_rmpath(self): with mock.patch( 'psutil.tests.os.stat', side_effect=OSError(errno.EINVAL, "") ) as m: - with self.assertRaises(OSError): + with pytest.raises(OSError): safe_rmpath(testfn) assert m.called @@ -203,8 +215,8 @@ def test_chdir(self): base = os.getcwd() os.mkdir(testfn) with chdir(testfn): - self.assertEqual(os.getcwd(), os.path.join(base, testfn)) - self.assertEqual(os.getcwd(), base) + assert os.getcwd() == os.path.join(base, testfn) + assert os.getcwd() == base class TestProcessUtils(PsutilTestCase): @@ -219,17 +231,17 @@ def test_reap_children(self): def test_spawn_children_pair(self): child, grandchild = self.spawn_children_pair() - self.assertNotEqual(child.pid, grandchild.pid) + assert child.pid != grandchild.pid assert child.is_running() assert grandchild.is_running() children = psutil.Process().children() - self.assertEqual(children, [child]) + assert children == [child] children = psutil.Process().children(recursive=True) - self.assertEqual(len(children), 2) - self.assertIn(child, children) - self.assertIn(grandchild, children) - self.assertEqual(child.ppid(), os.getpid()) - self.assertEqual(grandchild.ppid(), child.pid) + assert len(children) == 2 + assert child in children + assert grandchild in children + assert child.ppid() == os.getpid() + assert grandchild.ppid() == child.pid terminate(child) assert not child.is_running() @@ -238,10 +250,10 @@ def test_spawn_children_pair(self): terminate(grandchild) assert not grandchild.is_running() - @unittest.skipIf(not POSIX, "POSIX only") + @pytest.mark.skipif(not POSIX, reason="POSIX only") def test_spawn_zombie(self): _parent, zombie = self.spawn_zombie() - self.assertEqual(zombie.status(), psutil.STATUS_ZOMBIE) + assert zombie.status() == psutil.STATUS_ZOMBIE def test_terminate(self): # by subprocess.Popen @@ -287,23 +299,23 @@ class TestNetUtils(PsutilTestCase): def bind_socket(self): port = get_free_port() with contextlib.closing(bind_socket(addr=('', port))) as s: - self.assertEqual(s.getsockname()[1], port) + assert s.getsockname()[1] == port - @unittest.skipIf(not POSIX, "POSIX only") + @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): - self.assertEqual(sock.family, socket.AF_UNIX) - self.assertEqual(sock.type, socket.SOCK_STREAM) - self.assertEqual(sock.getsockname(), name) + assert sock.family == socket.AF_UNIX + assert sock.type == socket.SOCK_STREAM + assert sock.getsockname() == name assert os.path.exists(name) assert stat.S_ISSOCK(os.stat(name).st_mode) # UDP name = self.get_testfn() sock = bind_unix_socket(name, type=socket.SOCK_DGRAM) with contextlib.closing(sock): - self.assertEqual(sock.type, socket.SOCK_DGRAM) + assert sock.type == socket.SOCK_DGRAM def tcp_tcp_socketpair(self): addr = ("127.0.0.1", get_free_port()) @@ -312,34 +324,34 @@ def tcp_tcp_socketpair(self): with contextlib.closing(client): # Ensure they are connected and the positions are # correct. - self.assertEqual(server.getsockname(), addr) - self.assertEqual(client.getpeername(), addr) - self.assertNotEqual(client.getsockname(), addr) + assert server.getsockname() == addr + assert client.getpeername() == addr + assert client.getsockname() != addr - @unittest.skipIf(not POSIX, "POSIX only") - @unittest.skipIf( - NETBSD or FREEBSD, "/var/run/log UNIX socket opened by default" + @pytest.mark.skipif(not POSIX, reason="POSIX only") + @pytest.mark.skipif( + NETBSD or FREEBSD, reason="/var/run/log UNIX socket opened by default" ) def test_unix_socketpair(self): p = psutil.Process() num_fds = p.num_fds() - self.assertEqual( - filter_proc_net_connections(p.net_connections(kind='unix')), [] + assert ( + filter_proc_net_connections(p.net_connections(kind='unix')) == [] ) name = self.get_testfn() server, client = unix_socketpair(name) try: assert os.path.exists(name) assert stat.S_ISSOCK(os.stat(name).st_mode) - self.assertEqual(p.num_fds() - num_fds, 2) - self.assertEqual( + assert p.num_fds() - num_fds == 2 + assert ( len( filter_proc_net_connections(p.net_connections(kind='unix')) - ), - 2, + ) + == 2 ) - self.assertEqual(server.getsockname(), name) - self.assertEqual(client.getpeername(), name) + assert server.getsockname() == name + assert client.getpeername() == name finally: client.close() server.close() @@ -352,16 +364,16 @@ def test_create_sockets(self): fams[s.family] += 1 # work around http://bugs.python.org/issue30204 types[s.getsockopt(socket.SOL_SOCKET, socket.SO_TYPE)] += 1 - self.assertGreaterEqual(fams[socket.AF_INET], 2) + assert fams[socket.AF_INET] >= 2 if supports_ipv6(): - self.assertGreaterEqual(fams[socket.AF_INET6], 2) + assert fams[socket.AF_INET6] >= 2 if POSIX and HAS_NET_CONNECTIONS_UNIX: - self.assertGreaterEqual(fams[socket.AF_UNIX], 2) - self.assertGreaterEqual(types[socket.SOCK_STREAM], 2) - self.assertGreaterEqual(types[socket.SOCK_DGRAM], 2) + assert fams[socket.AF_UNIX] >= 2 + assert types[socket.SOCK_STREAM] >= 2 + assert types[socket.SOCK_DGRAM] >= 2 -@serialrun +@pytest.mark.xdist_group(name="serial") class TestMemLeakClass(TestMemoryLeak): @retry_on_failure() def test_times(self): @@ -370,29 +382,33 @@ def fun(): cnt = {'cnt': 0} self.execute(fun, times=10, warmup_times=15) - self.assertEqual(cnt['cnt'], 26) + assert cnt['cnt'] == 26 def test_param_err(self): - self.assertRaises(ValueError, self.execute, lambda: 0, times=0) - self.assertRaises(ValueError, self.execute, lambda: 0, times=-1) - self.assertRaises(ValueError, self.execute, lambda: 0, warmup_times=-1) - self.assertRaises(ValueError, self.execute, lambda: 0, tolerance=-1) - self.assertRaises(ValueError, self.execute, lambda: 0, retries=-1) + with pytest.raises(ValueError): + self.execute(lambda: 0, times=0) + with pytest.raises(ValueError): + self.execute(lambda: 0, times=-1) + with pytest.raises(ValueError): + self.execute(lambda: 0, warmup_times=-1) + with pytest.raises(ValueError): + self.execute(lambda: 0, tolerance=-1) + with pytest.raises(ValueError): + self.execute(lambda: 0, retries=-1) @retry_on_failure() - @unittest.skipIf(CI_TESTING, "skipped on CI") - @unittest.skipIf(COVERAGE, "skipped during test coverage") + @pytest.mark.skipif(CI_TESTING, reason="skipped on CI") + @pytest.mark.skipif(COVERAGE, reason="skipped during test coverage") def test_leak_mem(self): ls = [] def fun(ls=ls): - ls.append("x" * 124 * 1024) + ls.append("x" * 248 * 1024) try: - # will consume around 30M in total - self.assertRaisesRegex( - AssertionError, "extra-mem", self.execute, fun, times=50 - ) + # will consume around 60M in total + with pytest.raises(AssertionError, match="extra-mem"): + self.execute(fun, times=100) finally: del ls @@ -404,9 +420,8 @@ def fun(): box = [] kind = "fd" if POSIX else "handle" - self.assertRaisesRegex( - AssertionError, "unclosed " + kind, self.execute, fun - ) + with pytest.raises(AssertionError, match="unclosed " + kind): + self.execute(fun) def test_tolerance(self): def fun(): @@ -417,44 +432,156 @@ def fun(): self.execute( fun, times=times, warmup_times=0, tolerance=200 * 1024 * 1024 ) - self.assertEqual(len(ls), times + 1) + assert len(ls) == times + 1 def test_execute_w_exc(self): def fun_1(): 1 / 0 # noqa self.execute_w_exc(ZeroDivisionError, fun_1) - with self.assertRaises(ZeroDivisionError): + with pytest.raises(ZeroDivisionError): self.execute_w_exc(OSError, fun_1) def fun_2(): pass - with self.assertRaises(AssertionError): + with pytest.raises(AssertionError): self.execute_w_exc(ZeroDivisionError, fun_2) +class TestFakePytest(PsutilTestCase): + def run_test_class(self, klass): + suite = unittest.TestSuite() + suite.addTest(klass) + runner = unittest.TextTestRunner() + result = runner.run(suite) + return result + + def test_raises(self): + with fake_pytest.raises(ZeroDivisionError) as cm: + 1 / 0 # noqa + assert isinstance(cm.value, ZeroDivisionError) + + with fake_pytest.raises(ValueError, match="foo") as cm: + raise ValueError("foo") + + try: + with fake_pytest.raises(ValueError, match="foo") as cm: + raise ValueError("bar") + except AssertionError as err: + assert str(err) == '"foo" does not match "bar"' + else: + raise self.fail("exception not raised") + + def test_mark(self): + @fake_pytest.mark.xdist_group(name="serial") + def foo(): + return 1 + + assert foo() == 1 + + @fake_pytest.mark.xdist_group(name="serial") + class Foo: + def bar(self): + return 1 + + assert Foo().bar() == 1 + + def test_skipif(self): + class TestCase(unittest.TestCase): + @fake_pytest.mark.skipif(True, reason="reason") + def foo(self): + assert 1 == 1 # noqa + + result = self.run_test_class(TestCase("foo")) + assert result.wasSuccessful() + assert len(result.skipped) == 1 + assert result.skipped[0][1] == "reason" + + class TestCase(unittest.TestCase): + @fake_pytest.mark.skipif(False, reason="reason") + def foo(self): + assert 1 == 1 # noqa + + result = self.run_test_class(TestCase("foo")) + 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): + fake_pytest.skip("reason") + assert 1 == 0 # noqa + + result = self.run_test_class(TestCase("foo")) + assert result.wasSuccessful() + assert len(result.skipped) == 1 + assert result.skipped[0][1] == "reason" + + def test_main(self): + tmpdir = self.get_testfn(dir=HERE) + os.mkdir(tmpdir) + with open(os.path.join(tmpdir, "__init__.py"), "w"): + pass + with open(os.path.join(tmpdir, "test_file.py"), "w") as f: + f.write(textwrap.dedent("""\ + import unittest + + class TestCase(unittest.TestCase): + def test_passed(self): + pass + """).lstrip()) + with mock.patch.object(psutil.tests, "HERE", tmpdir): + with self.assertWarnsRegex( + UserWarning, "Fake pytest module was used" + ): + suite = fake_pytest.main() + assert suite.countTestCases() == 1 + + def test_warns(self): + # success + with fake_pytest.warns(UserWarning): + warnings.warn("foo", UserWarning, stacklevel=1) + + # failure + try: + with fake_pytest.warns(UserWarning): + warnings.warn("foo", DeprecationWarning, stacklevel=1) + except AssertionError: + pass + else: + raise self.fail("exception not raised") + + # match success + with fake_pytest.warns(UserWarning, match="foo"): + warnings.warn("foo", UserWarning, stacklevel=1) + + # match failure + try: + with fake_pytest.warns(UserWarning, match="foo"): + warnings.warn("bar", UserWarning, stacklevel=1) + except AssertionError: + pass + else: + raise self.fail("exception not raised") + + class TestTestingUtils(PsutilTestCase): def test_process_namespace(self): p = psutil.Process() ns = process_namespace(p) ns.test() fun = [x for x in ns.iter(ns.getters) if x[1] == 'ppid'][0][0] - self.assertEqual(fun(), p.ppid()) + assert fun() == p.ppid() def test_system_namespace(self): ns = system_namespace() fun = [x for x in ns.iter(ns.getters) if x[1] == 'net_if_addrs'][0][0] - self.assertEqual(fun(), psutil.net_if_addrs()) + assert fun() == psutil.net_if_addrs() class TestOtherUtils(PsutilTestCase): def test_is_namedtuple(self): assert is_namedtuple(collections.namedtuple('foo', 'a b c')(1, 2, 3)) assert not is_namedtuple(tuple()) - - -if __name__ == '__main__': - from psutil.tests.runner import run_from_name - - run_from_name(__file__) diff --git a/psutil/tests/test_unicode.py b/psutil/tests/test_unicode.py index 45aeb4e92..1bea46b5d 100755 --- a/psutil/tests/test_unicode.py +++ b/psutil/tests/test_unicode.py @@ -75,7 +75,6 @@ import os import shutil import traceback -import unittest import warnings from contextlib import closing @@ -101,9 +100,9 @@ from psutil.tests import copyload_shared_lib from psutil.tests import create_py_exe from psutil.tests import get_testfn +from psutil.tests import pytest from psutil.tests import safe_mkdir from psutil.tests import safe_rmpath -from psutil.tests import serialrun from psutil.tests import skip_on_access_denied from psutil.tests import spawn_testproc from psutil.tests import terminate @@ -175,12 +174,12 @@ def setUpClass(cls): def setUp(self): super().setUp() if self.skip_tests: - raise unittest.SkipTest("can't handle unicode str") + raise pytest.skip("can't handle unicode str") -@serialrun -@unittest.skipIf(ASCII_FS, "ASCII fs") -@unittest.skipIf(PYPY and not PY3, "too much trouble on PYPY2") +@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.""" @@ -205,11 +204,9 @@ def test_proc_exe(self): subp = self.spawn_testproc(cmd) p = psutil.Process(subp.pid) exe = p.exe() - self.assertIsInstance(exe, str) + assert isinstance(exe, str) if self.expect_exact_path_match(): - self.assertEqual( - os.path.normcase(exe), os.path.normcase(self.funky_name) - ) + assert os.path.normcase(exe) == os.path.normcase(self.funky_name) def test_proc_name(self): cmd = [ @@ -219,9 +216,9 @@ def test_proc_name(self): ] subp = self.spawn_testproc(cmd) name = psutil.Process(subp.pid).name() - self.assertIsInstance(name, str) + assert isinstance(name, str) if self.expect_exact_path_match(): - self.assertEqual(name, os.path.basename(self.funky_name)) + assert name == os.path.basename(self.funky_name) def test_proc_cmdline(self): cmd = [ @@ -233,9 +230,9 @@ def test_proc_cmdline(self): p = psutil.Process(subp.pid) cmdline = p.cmdline() for part in cmdline: - self.assertIsInstance(part, str) + assert isinstance(part, str) if self.expect_exact_path_match(): - self.assertEqual(cmdline, cmd) + assert cmdline == cmd def test_proc_cwd(self): dname = self.funky_name + "2" @@ -244,27 +241,25 @@ def test_proc_cwd(self): with chdir(dname): p = psutil.Process() cwd = p.cwd() - self.assertIsInstance(p.cwd(), str) + assert isinstance(p.cwd(), str) if self.expect_exact_path_match(): - self.assertEqual(cwd, dname) + assert cwd == dname - @unittest.skipIf(PYPY and WINDOWS, "fails on PYPY + WINDOWS") + @pytest.mark.skipif(PYPY and WINDOWS, reason="fails on PYPY + WINDOWS") def test_proc_open_files(self): p = psutil.Process() start = set(p.open_files()) with open(self.funky_name, 'rb'): new = set(p.open_files()) path = (new - start).pop().path - self.assertIsInstance(path, str) + assert isinstance(path, str) if BSD and not path: # XXX - see https://github.com/giampaolo/psutil/issues/595 - raise unittest.SkipTest("open_files on BSD is broken") + raise pytest.skip("open_files on BSD is broken") if self.expect_exact_path_match(): - self.assertEqual( - os.path.normcase(path), os.path.normcase(self.funky_name) - ) + assert os.path.normcase(path) == os.path.normcase(self.funky_name) - @unittest.skipIf(not POSIX, "POSIX only") + @pytest.mark.skipif(not POSIX, reason="POSIX only") def test_proc_net_connections(self): name = self.get_testfn(suffix=self.funky_suffix) try: @@ -273,14 +268,16 @@ def test_proc_net_connections(self): if PY3: raise else: - raise unittest.SkipTest("not supported") + raise pytest.skip("not supported") with closing(sock): conn = psutil.Process().net_connections('unix')[0] - self.assertIsInstance(conn.laddr, str) - self.assertEqual(conn.laddr, name) + assert isinstance(conn.laddr, str) + assert conn.laddr == name - @unittest.skipIf(not POSIX, "POSIX only") - @unittest.skipIf(not HAS_NET_CONNECTIONS_UNIX, "can't list UNIX sockets") + @pytest.mark.skipif(not POSIX, reason="POSIX only") + @pytest.mark.skipif( + not HAS_NET_CONNECTIONS_UNIX, reason="can't list UNIX sockets" + ) @skip_on_access_denied() def test_net_connections(self): def find_sock(cons): @@ -296,12 +293,12 @@ def find_sock(cons): if PY3: raise else: - raise unittest.SkipTest("not supported") + raise pytest.skip("not supported") with closing(sock): cons = psutil.net_connections(kind='unix') conn = find_sock(cons) - self.assertIsInstance(conn.laddr, str) - self.assertEqual(conn.laddr, name) + assert isinstance(conn.laddr, str) + assert conn.laddr == name def test_disk_usage(self): dname = self.funky_name + "2" @@ -309,9 +306,11 @@ def test_disk_usage(self): safe_mkdir(dname) psutil.disk_usage(dname) - @unittest.skipIf(not HAS_MEMORY_MAPS, "not supported") - @unittest.skipIf(not PY3, "ctypes does not support unicode on PY2") - @unittest.skipIf(PYPY, "unstable on PYPY") + @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. @@ -325,12 +324,12 @@ def normpath(p): ] # ...just to have a clearer msg in case of failure libpaths = [x for x in libpaths if TESTFN_PREFIX in x] - self.assertIn(normpath(funky_path), libpaths) + assert normpath(funky_path) in libpaths for path in libpaths: - self.assertIsInstance(path, str) + assert isinstance(path, str) -@unittest.skipIf(CI_TESTING, "unreliable on CI") +@pytest.mark.skipif(CI_TESTING, reason="unreliable on CI") class TestFSAPIsWithInvalidPath(TestFSAPIs): """Test FS APIs with a funky, invalid path name.""" @@ -351,8 +350,8 @@ class TestNonFSAPIS(BaseUnicodeTest): funky_suffix = UNICODE_SUFFIX if PY3 else 'รจ' - @unittest.skipIf(not HAS_ENVIRON, "not supported") - @unittest.skipIf(PYPY and WINDOWS, "segfaults on PYPY + WINDOWS") + @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 @@ -365,12 +364,6 @@ def test_proc_environ(self): p = psutil.Process(sproc.pid) env = p.environ() for k, v in env.items(): - self.assertIsInstance(k, str) - self.assertIsInstance(v, str) - self.assertEqual(env['FUNNY_ARG'], self.funky_suffix) - - -if __name__ == '__main__': - from psutil.tests.runner import run_from_name - - run_from_name(__file__) + assert isinstance(k, str) + assert isinstance(v, str) + assert env['FUNNY_ARG'] == self.funky_suffix diff --git a/psutil/tests/test_windows.py b/psutil/tests/test_windows.py index 7778cf4a6..b11b0e706 100755 --- a/psutil/tests/test_windows.py +++ b/psutil/tests/test_windows.py @@ -17,7 +17,6 @@ import subprocess import sys import time -import unittest import warnings import psutil @@ -35,6 +34,7 @@ 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 from psutil.tests import spawn_testproc @@ -47,7 +47,7 @@ import win32api # requires "pip install pywin32" import win32con import win32process - import wmi # requires "pip install wmi" / "make setup-dev-env" + import wmi # requires "pip install wmi" / "make install-pydeps-test" if WINDOWS: from psutil._pswindows import convert_oserror @@ -56,10 +56,12 @@ cext = psutil._psplatform.cext -@unittest.skipIf(not WINDOWS, "WINDOWS only") -@unittest.skipIf(PYPY, "pywin32 not available on PYPY") +@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 -@unittest.skipIf(GITHUB_ACTIONS and not PY3, "pywin32 broken on GITHUB + PY2") +@pytest.mark.skipif( + GITHUB_ACTIONS and not PY3, reason="pywin32 broken on GITHUB + PY2" +) class WindowsTestCase(PsutilTestCase): pass @@ -71,7 +73,7 @@ def powershell(cmd): "Get-CIMInstance Win32_PageFileUsage | Select AllocatedBaseSize") """ if not which("powershell.exe"): - raise unittest.SkipTest("powershell.exe not available") + raise pytest.skip("powershell.exe not available") cmdline = ( 'powershell.exe -ExecutionPolicy Bypass -NoLogo -NonInteractive ' + '-NoProfile -WindowStyle Hidden -Command "%s"' % cmd @@ -102,45 +104,43 @@ def wmic(path, what, converter=int): class TestCpuAPIs(WindowsTestCase): - @unittest.skipIf( + @pytest.mark.skipif( 'NUMBER_OF_PROCESSORS' not in os.environ, - 'NUMBER_OF_PROCESSORS env var is not available', + reason="NUMBER_OF_PROCESSORS env var is not available", ) def test_cpu_count_vs_NUMBER_OF_PROCESSORS(self): # Will likely fail on many-cores systems: # https://stackoverflow.com/questions/31209256 num_cpus = int(os.environ['NUMBER_OF_PROCESSORS']) - self.assertEqual(num_cpus, psutil.cpu_count()) + assert num_cpus == psutil.cpu_count() def test_cpu_count_vs_GetSystemInfo(self): # Will likely fail on many-cores systems: # https://stackoverflow.com/questions/31209256 sys_value = win32api.GetSystemInfo()[5] psutil_value = psutil.cpu_count() - self.assertEqual(sys_value, psutil_value) + assert sys_value == psutil_value def test_cpu_count_logical_vs_wmi(self): w = wmi.WMI() procs = sum( proc.NumberOfLogicalProcessors for proc in w.Win32_Processor() ) - self.assertEqual(psutil.cpu_count(), procs) + assert psutil.cpu_count() == procs def test_cpu_count_cores_vs_wmi(self): w = wmi.WMI() cores = sum(proc.NumberOfCores for proc in w.Win32_Processor()) - self.assertEqual(psutil.cpu_count(logical=False), cores) + assert psutil.cpu_count(logical=False) == cores def test_cpu_count_vs_cpu_times(self): - self.assertEqual( - psutil.cpu_count(), len(psutil.cpu_times(percpu=True)) - ) + assert psutil.cpu_count() == len(psutil.cpu_times(percpu=True)) def test_cpu_freq(self): w = wmi.WMI() proc = w.Win32_Processor()[0] - self.assertEqual(proc.CurrentClockSpeed, psutil.cpu_freq().current) - self.assertEqual(proc.MaxClockSpeed, psutil.cpu_freq().max) + assert proc.CurrentClockSpeed == psutil.cpu_freq().current + assert proc.MaxClockSpeed == psutil.cpu_freq().max class TestSystemAPIs(WindowsTestCase): @@ -157,27 +157,24 @@ def test_nic_names(self): def test_total_phymem(self): w = wmi.WMI().Win32_ComputerSystem()[0] - self.assertEqual( - int(w.TotalPhysicalMemory), psutil.virtual_memory().total - ) + assert int(w.TotalPhysicalMemory) == psutil.virtual_memory().total def test_free_phymem(self): w = wmi.WMI().Win32_PerfRawData_PerfOS_Memory()[0] - self.assertAlmostEqual( - int(w.AvailableBytes), - psutil.virtual_memory().free, - delta=TOLERANCE_SYS_MEM, + assert ( + abs(int(w.AvailableBytes) - psutil.virtual_memory().free) + < TOLERANCE_SYS_MEM ) def test_total_swapmem(self): w = wmi.WMI().Win32_PerfRawData_PerfOS_Memory()[0] - self.assertEqual( - int(w.CommitLimit) - psutil.virtual_memory().total, - psutil.swap_memory().total, + assert ( + int(w.CommitLimit) - psutil.virtual_memory().total + == psutil.swap_memory().total ) if psutil.swap_memory().total == 0: - self.assertEqual(0, psutil.swap_memory().free) - self.assertEqual(0, psutil.swap_memory().used) + assert psutil.swap_memory().free == 0 + assert psutil.swap_memory().used == 0 def test_percent_swapmem(self): if psutil.swap_memory().total > 0: @@ -186,13 +183,11 @@ def test_percent_swapmem(self): percentSwap = int(w.PercentUsage) * 100 / int(w.PercentUsage_Base) # exact percent may change but should be reasonable # assert within +/- 5% and between 0 and 100% - self.assertGreaterEqual(psutil.swap_memory().percent, 0) - self.assertAlmostEqual( - psutil.swap_memory().percent, percentSwap, delta=5 - ) - self.assertLessEqual(psutil.swap_memory().percent, 100) + assert psutil.swap_memory().percent >= 0 + assert abs(psutil.swap_memory().percent - percentSwap) < 5 + assert psutil.swap_memory().percent <= 100 - # @unittest.skipIf(wmi is None, "wmi module is not installed") + # @pytest.mark.skipif(wmi is None, reason="wmi module is not installed") # def test__UPTIME(self): # # _UPTIME constant is not public but it is used internally # # as value to return for pid 0 creation time. @@ -204,7 +199,7 @@ def test_percent_swapmem(self): # time.localtime(p.create_time())) # Note: this test is not very reliable - @unittest.skipIf(APPVEYOR, "test not relieable on appveyor") + @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 @@ -212,7 +207,7 @@ def test_pids(self): w = wmi.WMI().Win32_Process() wmi_pids = set([x.ProcessId for x in w]) psutil_pids = set(psutil.pids()) - self.assertEqual(wmi_pids, psutil_pids) + assert wmi_pids == psutil_pids @retry_on_failure() def test_disks(self): @@ -233,9 +228,9 @@ def test_disks(self): except FileNotFoundError: # usually this is the floppy break - self.assertEqual(usage.total, int(wmi_part.Size)) + assert usage.total == int(wmi_part.Size) wmi_free = int(wmi_part.FreeSpace) - self.assertEqual(usage.free, wmi_free) + assert usage.free == wmi_free # 10 MB tolerance if abs(usage.free - wmi_free) > 10 * 1024 * 1024: raise self.fail( @@ -252,15 +247,11 @@ def test_disk_usage(self): continue sys_value = win32api.GetDiskFreeSpaceEx(disk.mountpoint) psutil_value = psutil.disk_usage(disk.mountpoint) - self.assertAlmostEqual( - sys_value[0], psutil_value.free, delta=TOLERANCE_DISK_USAGE - ) - self.assertAlmostEqual( - sys_value[1], psutil_value.total, delta=TOLERANCE_DISK_USAGE - ) - self.assertEqual( - psutil_value.used, psutil_value.total - psutil_value.free + assert abs(sys_value[0] - psutil_value.free) < TOLERANCE_DISK_USAGE + assert ( + abs(sys_value[1] - psutil_value.total) < TOLERANCE_DISK_USAGE ) + assert psutil_value.used == psutil_value.total - psutil_value.free def test_disk_partitions(self): sys_value = [ @@ -273,7 +264,7 @@ def test_disk_partitions(self): for x in psutil.disk_partitions(all=True) if not x.mountpoint.startswith('A:') ] - self.assertEqual(sys_value, psutil_value) + assert sys_value == psutil_value def test_net_if_stats(self): ps_names = set(cext.net_if_stats()) @@ -282,9 +273,9 @@ def test_net_if_stats(self): for wmi_adapter in wmi_adapters: wmi_names.add(wmi_adapter.Name) wmi_names.add(wmi_adapter.NetConnectionID) - self.assertTrue( - ps_names & wmi_names, - "no common entries in %s, %s" % (ps_names, wmi_names), + assert ps_names & wmi_names, "no common entries in %s, %s" % ( + ps_names, + wmi_names, ) def test_boot_time(self): @@ -295,18 +286,18 @@ def test_boot_time(self): ) psutil_dt = datetime.datetime.fromtimestamp(psutil.boot_time()) diff = abs((wmi_btime_dt - psutil_dt).total_seconds()) - self.assertLessEqual(diff, 5) + assert diff <= 5 def test_boot_time_fluctuation(self): # https://github.com/giampaolo/psutil/issues/1007 with mock.patch('psutil._pswindows.cext.boot_time', return_value=5): - self.assertEqual(psutil.boot_time(), 5) + assert psutil.boot_time() == 5 with mock.patch('psutil._pswindows.cext.boot_time', return_value=4): - self.assertEqual(psutil.boot_time(), 5) + assert psutil.boot_time() == 5 with mock.patch('psutil._pswindows.cext.boot_time', return_value=6): - self.assertEqual(psutil.boot_time(), 5) + assert psutil.boot_time() == 5 with mock.patch('psutil._pswindows.cext.boot_time', return_value=333): - self.assertEqual(psutil.boot_time(), 333) + assert psutil.boot_time() == 333 # =================================================================== @@ -317,46 +308,44 @@ def test_boot_time_fluctuation(self): class TestSensorsBattery(WindowsTestCase): def test_has_battery(self): if win32api.GetPwrCapabilities()['SystemBatteriesPresent']: - self.assertIsNotNone(psutil.sensors_battery()) + assert psutil.sensors_battery() is not None else: - self.assertIsNone(psutil.sensors_battery()) + assert psutil.sensors_battery() is None - @unittest.skipIf(not HAS_BATTERY, "no battery") + @pytest.mark.skipif(not HAS_BATTERY, reason="no battery") def test_percent(self): w = wmi.WMI() battery_wmi = w.query('select * from Win32_Battery')[0] battery_psutil = psutil.sensors_battery() - self.assertAlmostEqual( - battery_psutil.percent, - battery_wmi.EstimatedChargeRemaining, - delta=1, + assert ( + abs(battery_psutil.percent - battery_wmi.EstimatedChargeRemaining) + < 1 ) - @unittest.skipIf(not HAS_BATTERY, "no battery") + @pytest.mark.skipif(not HAS_BATTERY, reason="no battery") def test_power_plugged(self): w = wmi.WMI() battery_wmi = w.query('select * from Win32_Battery')[0] battery_psutil = psutil.sensors_battery() # Status codes: # https://msdn.microsoft.com/en-us/library/aa394074(v=vs.85).aspx - self.assertEqual( - battery_psutil.power_plugged, battery_wmi.BatteryStatus == 2 - ) + assert battery_psutil.power_plugged == (battery_wmi.BatteryStatus == 2) def test_emulate_no_battery(self): with mock.patch( "psutil._pswindows.cext.sensors_battery", return_value=(0, 128, 0, 0), ) as m: - self.assertIsNone(psutil.sensors_battery()) + assert psutil.sensors_battery() is None assert m.called def test_emulate_power_connected(self): with mock.patch( "psutil._pswindows.cext.sensors_battery", return_value=(1, 0, 0, 0) ) as m: - self.assertEqual( - psutil.sensors_battery().secsleft, psutil.POWER_TIME_UNLIMITED + assert ( + psutil.sensors_battery().secsleft + == psutil.POWER_TIME_UNLIMITED ) assert m.called @@ -364,8 +353,9 @@ def test_emulate_power_charging(self): with mock.patch( "psutil._pswindows.cext.sensors_battery", return_value=(0, 8, 0, 0) ) as m: - self.assertEqual( - psutil.sensors_battery().secsleft, psutil.POWER_TIME_UNLIMITED + assert ( + psutil.sensors_battery().secsleft + == psutil.POWER_TIME_UNLIMITED ) assert m.called @@ -374,8 +364,8 @@ def test_emulate_secs_left_unknown(self): "psutil._pswindows.cext.sensors_battery", return_value=(0, 0, 0, -1), ) as m: - self.assertEqual( - psutil.sensors_battery().secsleft, psutil.POWER_TIME_UNKNOWN + assert ( + psutil.sensors_battery().secsleft == psutil.POWER_TIME_UNKNOWN ) assert m.called @@ -396,16 +386,17 @@ def tearDownClass(cls): def test_issue_24(self): p = psutil.Process(0) - self.assertRaises(psutil.AccessDenied, p.kill) + with pytest.raises(psutil.AccessDenied): + p.kill() def test_special_pid(self): p = psutil.Process(4) - self.assertEqual(p.name(), 'System') + assert p.name() == 'System' # use __str__ to access all common Process properties to check # that nothing strange happens str(p) p.username() - self.assertGreaterEqual(p.create_time(), 0.0) + assert p.create_time() >= 0.0 try: rss, _vms = p.memory_info()[:2] except psutil.AccessDenied: @@ -413,11 +404,12 @@ def test_special_pid(self): if platform.uname()[1] not in ('vista', 'win-7', 'win7'): raise else: - self.assertGreater(rss, 0) + assert rss > 0 def test_send_signal(self): p = psutil.Process(self.pid) - self.assertRaises(ValueError, p.send_signal, signal.SIGINT) + with pytest.raises(ValueError): + p.send_signal(signal.SIGINT) def test_num_handles_increment(self): p = psutil.Process(os.getpid()) @@ -426,9 +418,9 @@ def test_num_handles_increment(self): win32con.PROCESS_QUERY_INFORMATION, win32con.FALSE, os.getpid() ) after = p.num_handles() - self.assertEqual(after, before + 1) + assert after == before + 1 win32api.CloseHandle(handle) - self.assertEqual(p.num_handles(), before) + assert p.num_handles() == before def test_ctrl_signals(self): p = psutil.Process(self.spawn_testproc().pid) @@ -436,12 +428,10 @@ def test_ctrl_signals(self): p.send_signal(signal.CTRL_BREAK_EVENT) p.kill() p.wait() - self.assertRaises( - psutil.NoSuchProcess, p.send_signal, signal.CTRL_C_EVENT - ) - self.assertRaises( - psutil.NoSuchProcess, p.send_signal, signal.CTRL_BREAK_EVENT - ) + with pytest.raises(psutil.NoSuchProcess): + p.send_signal(signal.CTRL_C_EVENT) + with pytest.raises(psutil.NoSuchProcess): + p.send_signal(signal.CTRL_BREAK_EVENT) def test_username(self): name = win32api.GetUserNameEx(win32con.NameSamCompatible) @@ -449,8 +439,8 @@ def test_username(self): # When running as a service account (most likely to be # NetworkService), these user name calculations don't produce the # same result, causing the test to fail. - raise unittest.SkipTest('running as service account') - self.assertEqual(psutil.Process().username(), name) + raise pytest.skip('running as service account') + assert psutil.Process().username() == name def test_cmdline(self): sys_value = re.sub('[ ]+', ' ', win32api.GetCommandLine()).strip() @@ -461,7 +451,7 @@ def test_cmdline(self): # the first 2 quotes from sys_value if not in psutil_value. # A path to an executable will not contain quotes, so this is safe. sys_value = sys_value.replace('"', '', 2) - self.assertEqual(sys_value, psutil_value) + assert sys_value == psutil_value # XXX - occasional failures @@ -485,7 +475,7 @@ def test_nice(self): self.addCleanup(win32api.CloseHandle, handle) sys_value = win32process.GetPriorityClass(handle) psutil_value = psutil.Process().nice() - self.assertEqual(psutil_value, sys_value) + assert psutil_value == sys_value def test_memory_info(self): handle = win32api.OpenProcess( @@ -494,30 +484,25 @@ def test_memory_info(self): self.addCleanup(win32api.CloseHandle, handle) sys_value = win32process.GetProcessMemoryInfo(handle) psutil_value = psutil.Process(self.pid).memory_info() - self.assertEqual( - sys_value['PeakWorkingSetSize'], psutil_value.peak_wset - ) - self.assertEqual(sys_value['WorkingSetSize'], psutil_value.wset) - self.assertEqual( - sys_value['QuotaPeakPagedPoolUsage'], psutil_value.peak_paged_pool - ) - self.assertEqual( - sys_value['QuotaPagedPoolUsage'], psutil_value.paged_pool + assert sys_value['PeakWorkingSetSize'] == psutil_value.peak_wset + assert sys_value['WorkingSetSize'] == psutil_value.wset + assert ( + sys_value['QuotaPeakPagedPoolUsage'] + == psutil_value.peak_paged_pool ) - self.assertEqual( - sys_value['QuotaPeakNonPagedPoolUsage'], - psutil_value.peak_nonpaged_pool, + assert sys_value['QuotaPagedPoolUsage'] == psutil_value.paged_pool + assert ( + sys_value['QuotaPeakNonPagedPoolUsage'] + == psutil_value.peak_nonpaged_pool ) - self.assertEqual( - sys_value['QuotaNonPagedPoolUsage'], psutil_value.nonpaged_pool - ) - self.assertEqual(sys_value['PagefileUsage'], psutil_value.pagefile) - self.assertEqual( - sys_value['PeakPagefileUsage'], psutil_value.peak_pagefile + assert ( + sys_value['QuotaNonPagedPoolUsage'] == psutil_value.nonpaged_pool ) + assert sys_value['PagefileUsage'] == psutil_value.pagefile + assert sys_value['PeakPagefileUsage'] == psutil_value.peak_pagefile - self.assertEqual(psutil_value.rss, psutil_value.wset) - self.assertEqual(psutil_value.vms, psutil_value.pagefile) + assert psutil_value.rss == psutil_value.wset + assert psutil_value.vms == psutil_value.pagefile def test_wait(self): handle = win32api.OpenProcess( @@ -528,7 +513,7 @@ def test_wait(self): p.terminate() psutil_value = p.wait() sys_value = win32process.GetExitCodeProcess(handle) - self.assertEqual(psutil_value, sys_value) + assert psutil_value == sys_value def test_cpu_affinity(self): def from_bitmask(x): @@ -542,7 +527,7 @@ def from_bitmask(x): win32process.GetProcessAffinityMask(handle)[0] ) psutil_value = psutil.Process(self.pid).cpu_affinity() - self.assertEqual(psutil_value, sys_value) + assert psutil_value == sys_value def test_io_counters(self): handle = win32api.OpenProcess( @@ -551,24 +536,12 @@ def test_io_counters(self): self.addCleanup(win32api.CloseHandle, handle) sys_value = win32process.GetProcessIoCounters(handle) psutil_value = psutil.Process().io_counters() - self.assertEqual( - psutil_value.read_count, sys_value['ReadOperationCount'] - ) - self.assertEqual( - psutil_value.write_count, sys_value['WriteOperationCount'] - ) - self.assertEqual( - psutil_value.read_bytes, sys_value['ReadTransferCount'] - ) - self.assertEqual( - psutil_value.write_bytes, sys_value['WriteTransferCount'] - ) - self.assertEqual( - psutil_value.other_count, sys_value['OtherOperationCount'] - ) - self.assertEqual( - psutil_value.other_bytes, sys_value['OtherTransferCount'] - ) + assert psutil_value.read_count == sys_value['ReadOperationCount'] + assert psutil_value.write_count == sys_value['WriteOperationCount'] + assert psutil_value.read_bytes == sys_value['ReadTransferCount'] + assert psutil_value.write_bytes == sys_value['WriteTransferCount'] + assert psutil_value.other_count == sys_value['OtherOperationCount'] + assert psutil_value.other_bytes == sys_value['OtherTransferCount'] def test_num_handles(self): import ctypes @@ -586,7 +559,7 @@ def test_num_handles(self): ) sys_value = hndcnt.value psutil_value = psutil.Process(self.pid).num_handles() - self.assertEqual(psutil_value, sys_value) + assert psutil_value == sys_value def test_error_partial_copy(self): # https://github.com/giampaolo/psutil/issues/875 @@ -595,15 +568,17 @@ def test_error_partial_copy(self): with mock.patch("psutil._psplatform.cext.proc_cwd", side_effect=exc): with mock.patch("time.sleep") as m: p = psutil.Process() - self.assertRaises(psutil.AccessDenied, p.cwd) - self.assertGreaterEqual(m.call_count, 5) + with pytest.raises(psutil.AccessDenied): + p.cwd() + assert m.call_count >= 5 def test_exe(self): # NtQuerySystemInformation succeeds if process is gone. Make sure # it raises NSP for a non existent pid. pid = psutil.pids()[-1] + 99999 proc = psutil._psplatform.Process(pid) - self.assertRaises(psutil.NoSuchProcess, proc.exe) + with pytest.raises(psutil.NoSuchProcess): + proc.exe() class TestProcessWMI(WindowsTestCase): @@ -620,35 +595,37 @@ def tearDownClass(cls): def test_name(self): w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] p = psutil.Process(self.pid) - self.assertEqual(p.name(), w.Caption) + assert p.name() == w.Caption # This fail on github because using virtualenv for test environment - @unittest.skipIf(GITHUB_ACTIONS, "unreliable path on GITHUB_ACTIONS") + @pytest.mark.skipif( + GITHUB_ACTIONS, reason="unreliable path on GITHUB_ACTIONS" + ) def test_exe(self): w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] p = psutil.Process(self.pid) # Note: wmi reports the exe as a lower case string. # Being Windows paths case-insensitive we ignore that. - self.assertEqual(p.exe().lower(), w.ExecutablePath.lower()) + assert p.exe().lower() == w.ExecutablePath.lower() def test_cmdline(self): w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] p = psutil.Process(self.pid) - self.assertEqual(' '.join(p.cmdline()), w.CommandLine.replace('"', '')) + assert ' '.join(p.cmdline()) == w.CommandLine.replace('"', '') def test_username(self): w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] p = psutil.Process(self.pid) domain, _, username = w.GetOwner() username = "%s\\%s" % (domain, username) - self.assertEqual(p.username(), username) + assert p.username() == username @retry_on_failure() def test_memory_rss(self): w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] p = psutil.Process(self.pid) rss = p.memory_info().rss - self.assertEqual(rss, int(w.WorkingSetSize)) + assert rss == int(w.WorkingSetSize) @retry_on_failure() def test_memory_vms(self): @@ -670,13 +647,13 @@ def test_create_time(self): psutil_create = time.strftime( "%Y%m%d%H%M%S", time.localtime(p.create_time()) ) - self.assertEqual(wmic_create, psutil_create) + assert wmic_create == psutil_create # --- -@unittest.skipIf(not WINDOWS, "WINDOWS only") +@pytest.mark.skipif(not WINDOWS, reason="WINDOWS only") class TestDualProcessImplementation(PsutilTestCase): """Certain APIs on Windows have 2 internal implementations, one based on documented Windows APIs, another one based @@ -702,11 +679,11 @@ def test_memory_info(self): side_effect=OSError(errno.EPERM, "msg"), ) as fun: mem_2 = psutil.Process(self.pid).memory_info() - self.assertEqual(len(mem_1), len(mem_2)) + assert len(mem_1) == len(mem_2) for i in range(len(mem_1)): - self.assertGreaterEqual(mem_1[i], 0) - self.assertGreaterEqual(mem_2[i], 0) - self.assertAlmostEqual(mem_1[i], mem_2[i], delta=512) + assert mem_1[i] >= 0 + assert mem_2[i] >= 0 + assert abs(mem_1[i] - mem_2[i]) < 512 assert fun.called def test_create_time(self): @@ -715,7 +692,7 @@ def test_create_time(self): "psutil._psplatform.cext.proc_times", side_effect=OSError(errno.EPERM, "msg"), ) as fun: - self.assertEqual(psutil.Process(self.pid).create_time(), ctime) + assert psutil.Process(self.pid).create_time() == ctime assert fun.called def test_cpu_times(self): @@ -726,12 +703,8 @@ def test_cpu_times(self): ) as fun: cpu_times_2 = psutil.Process(self.pid).cpu_times() assert fun.called - self.assertAlmostEqual( - cpu_times_1.user, cpu_times_2.user, delta=0.01 - ) - self.assertAlmostEqual( - cpu_times_1.system, cpu_times_2.system, delta=0.01 - ) + assert abs(cpu_times_1.user - cpu_times_2.user) < 0.01 + assert abs(cpu_times_1.system - cpu_times_2.system) < 0.01 def test_io_counters(self): io_counters_1 = psutil.Process(self.pid).io_counters() @@ -741,9 +714,7 @@ def test_io_counters(self): ) as fun: io_counters_2 = psutil.Process(self.pid).io_counters() for i in range(len(io_counters_1)): - self.assertAlmostEqual( - io_counters_1[i], io_counters_2[i], delta=5 - ) + assert abs(io_counters_1[i] - io_counters_2[i]) < 5 assert fun.called def test_num_handles(self): @@ -752,9 +723,7 @@ def test_num_handles(self): "psutil._psplatform.cext.proc_num_handles", side_effect=OSError(errno.EPERM, "msg"), ) as fun: - self.assertEqual( - psutil.Process(self.pid).num_handles(), num_handles - ) + assert psutil.Process(self.pid).num_handles() == num_handles assert fun.called def test_cmdline(self): @@ -769,10 +738,10 @@ def test_cmdline(self): ): raise else: - self.assertEqual(a, b) + assert a == b -@unittest.skipIf(not WINDOWS, "WINDOWS only") +@pytest.mark.skipif(not WINDOWS, reason="WINDOWS only") class RemoteProcessTestCase(PsutilTestCase): """Certain functions require calling ReadProcessMemory. This trivially works when called on the current process. @@ -805,7 +774,7 @@ def setUp(self): other_python = self.find_other_interpreter() if other_python is None: - raise unittest.SkipTest( + raise pytest.skip( "could not find interpreter with opposite bitness" ) if IS_64BIT: @@ -831,27 +800,27 @@ def tearDown(self): def test_cmdline_32(self): p = psutil.Process(self.proc32.pid) - self.assertEqual(len(p.cmdline()), 3) - self.assertEqual(p.cmdline()[1:], self.test_args) + assert len(p.cmdline()) == 3 + assert p.cmdline()[1:] == self.test_args def test_cmdline_64(self): p = psutil.Process(self.proc64.pid) - self.assertEqual(len(p.cmdline()), 3) - self.assertEqual(p.cmdline()[1:], self.test_args) + assert len(p.cmdline()) == 3 + assert p.cmdline()[1:] == self.test_args def test_cwd_32(self): p = psutil.Process(self.proc32.pid) - self.assertEqual(p.cwd(), os.getcwd()) + assert p.cwd() == os.getcwd() def test_cwd_64(self): p = psutil.Process(self.proc64.pid) - self.assertEqual(p.cwd(), os.getcwd()) + assert p.cwd() == os.getcwd() def test_environ_32(self): p = psutil.Process(self.proc32.pid) e = p.environ() - self.assertIn("THINK_OF_A_NUMBER", e) - self.assertEqual(e["THINK_OF_A_NUMBER"], str(os.getpid())) + assert "THINK_OF_A_NUMBER" in e + assert e["THINK_OF_A_NUMBER"] == str(os.getpid()) def test_environ_64(self): p = psutil.Process(self.proc64.pid) @@ -866,7 +835,7 @@ def test_environ_64(self): # =================================================================== -@unittest.skipIf(not WINDOWS, "WINDOWS only") +@pytest.mark.skipif(not WINDOWS, reason="WINDOWS only") class TestServices(PsutilTestCase): def test_win_service_iter(self): valid_statuses = set([ @@ -890,27 +859,27 @@ def test_win_service_iter(self): ]) for serv in psutil.win_service_iter(): data = serv.as_dict() - self.assertIsInstance(data['name'], str) - self.assertNotEqual(data['name'].strip(), "") - self.assertIsInstance(data['display_name'], str) - self.assertIsInstance(data['username'], str) - self.assertIn(data['status'], valid_statuses) + assert isinstance(data['name'], str) + assert data['name'].strip() + assert isinstance(data['display_name'], str) + assert isinstance(data['username'], str) + assert data['status'] in valid_statuses if data['pid'] is not None: psutil.Process(data['pid']) - self.assertIsInstance(data['binpath'], str) - self.assertIsInstance(data['username'], str) - self.assertIsInstance(data['start_type'], str) - self.assertIn(data['start_type'], valid_start_types) - self.assertIn(data['status'], valid_statuses) - self.assertIsInstance(data['description'], str) + assert isinstance(data['binpath'], str) + assert isinstance(data['username'], str) + assert isinstance(data['start_type'], str) + assert data['start_type'] in valid_start_types + assert data['status'] in valid_statuses + assert isinstance(data['description'], str) pid = serv.pid() if pid is not None: p = psutil.Process(pid) - self.assertTrue(p.is_running()) + assert p.is_running() # win_service_get s = psutil.win_service_get(serv.name()) # test __eq__ - self.assertEqual(serv, s) + assert serv == s def test_win_service_get(self): ERROR_SERVICE_DOES_NOT_EXIST = ( @@ -919,9 +888,9 @@ def test_win_service_get(self): ERROR_ACCESS_DENIED = psutil._psplatform.cext.ERROR_ACCESS_DENIED name = next(psutil.win_service_iter()).name() - with self.assertRaises(psutil.NoSuchProcess) as cm: + with pytest.raises(psutil.NoSuchProcess) as cm: psutil.win_service_get(name + '???') - self.assertEqual(cm.exception.name, name + '???') + assert cm.value.name == name + '???' # test NoSuchProcess service = psutil.win_service_get(name) @@ -933,11 +902,13 @@ def test_win_service_get(self): with mock.patch( "psutil._psplatform.cext.winservice_query_status", side_effect=exc ): - self.assertRaises(psutil.NoSuchProcess, service.status) + with pytest.raises(psutil.NoSuchProcess): + service.status() with mock.patch( "psutil._psplatform.cext.winservice_query_config", side_effect=exc ): - self.assertRaises(psutil.NoSuchProcess, service.username) + with pytest.raises(psutil.NoSuchProcess): + service.username() # test AccessDenied if PY3: @@ -948,20 +919,16 @@ def test_win_service_get(self): with mock.patch( "psutil._psplatform.cext.winservice_query_status", side_effect=exc ): - self.assertRaises(psutil.AccessDenied, service.status) + with pytest.raises(psutil.AccessDenied): + service.status() with mock.patch( "psutil._psplatform.cext.winservice_query_config", side_effect=exc ): - self.assertRaises(psutil.AccessDenied, service.username) + with pytest.raises(psutil.AccessDenied): + service.username() # test __str__ and __repr__ - self.assertIn(service.name(), str(service)) - self.assertIn(service.display_name(), str(service)) - self.assertIn(service.name(), repr(service)) - self.assertIn(service.display_name(), repr(service)) - - -if __name__ == '__main__': - from psutil.tests.runner import run_from_name - - run_from_name(__file__) + assert service.name() in str(service) + assert service.display_name() in str(service) + assert service.name() in repr(service) + assert service.display_name() in repr(service) diff --git a/pyproject.toml b/pyproject.toml index 04ad6414d..988ff9c01 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -110,7 +110,6 @@ ignore = [ ".github/workflows/*" = ["T201", "T203"] "psutil/_compat.py" = ["PLW0127"] # self-assigning-variable "psutil/tests/*" = ["EM101", "TRY003"] -"psutil/tests/runner.py" = ["T201", "T203"] "scripts/*" = ["T201", "T203"] "scripts/internal/*" = ["EM101", "T201", "T203", "TRY003"] "setup.py" = ["T201", "T203"] @@ -212,18 +211,10 @@ skip = [ "cp3{7,8,9,10,11,12}-*linux_{aarch64,ppc64le,s390x}", # Only test cp36/cp313 on qemu tested architectures "pp*", ] -test-command = [ - "env PYTHONWARNINGS=always PYTHONUNBUFFERED=1 PSUTIL_DEBUG=1 PSUTIL_SCRIPTS_DIR={project}/scripts python {project}/psutil/tests/runner.py", - "env PYTHONWARNINGS=always PYTHONUNBUFFERED=1 PSUTIL_DEBUG=1 PSUTIL_SCRIPTS_DIR={project}/scripts python {project}/psutil/tests/test_memleaks.py", -] -test-extras = "test" [tool.cibuildwheel.macos] archs = ["arm64", "x86_64"] -[tool.cibuildwheel.linux] -before-all = "yum install -y net-tools" - [build-system] build-backend = "setuptools.build_meta" -requires = ["setuptools>=43", "wheel"] +requires = ["setuptools>=43"] diff --git a/scripts/internal/install-sysdeps.sh b/scripts/internal/install-sysdeps.sh new file mode 100755 index 000000000..003a7d081 --- /dev/null +++ b/scripts/internal/install-sysdeps.sh @@ -0,0 +1,62 @@ +#!/bin/sh + +# Depending on the UNIX platform, install the necessary system dependencies to: +# * compile psutil +# * run those unit tests that rely on CLI tools (netstat, ps, etc.) +# NOTE: this script MUST be kept compatible with the `sh` shell. + +set -e + +UNAME_S=$(uname -s) + +case "$UNAME_S" in + Linux) + LINUX=true + if command -v apt > /dev/null 2>&1; then + HAS_APT=true + elif command -v yum > /dev/null 2>&1; then + HAS_YUM=true + elif command -v apk > /dev/null 2>&1; then + HAS_APK=true # musl linux + fi + ;; + FreeBSD) + FREEBSD=true + ;; + NetBSD) + NETBSD=true + ;; + OpenBSD) + OPENBSD=true + ;; +esac + +# Check if running as root +if [ "$(id -u)" -ne 0 ]; then + SUDO=sudo +fi + +# Function to install system dependencies +main() { + if [ $HAS_APT ]; then + $SUDO apt-get install -y python3-dev gcc + $SUDO apt-get install -y net-tools coreutils util-linux # for tests + elif [ $HAS_YUM ]; then + $SUDO yum install -y python3-devel gcc + $SUDO yum install -y net-tools coreutils util-linux # for tests + elif [ $HAS_APK ]; then + $SUDO apk add python3-dev gcc musl-dev linux-headers coreutils procps + elif [ $FREEBSD ]; then + $SUDO pkg install -y python3 gcc + elif [ $NETBSD ]; then + $SUDO /usr/sbin/pkg_add -v pkgin + $SUDO pkgin update + $SUDO pkgin -y install python311-* gcc12-* + elif [ $OPENBSD ]; then + $SUDO pkg_add gcc python3 + else + echo "Unsupported platform: $UNAME_S" + fi +} + +main diff --git a/scripts/internal/install_pip.py b/scripts/internal/install_pip.py new file mode 100755 index 000000000..44557529a --- /dev/null +++ b/scripts/internal/install_pip.py @@ -0,0 +1,58 @@ +#!/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. + +import sys + + +try: + import pip # noqa: F401 +except ImportError: + pass +else: + print("pip already installed") + sys.exit(0) + +import os +import ssl +import tempfile + + +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" + + +def main(): + ssl_context = ( + ssl._create_unverified_context() + if hasattr(ssl, "_create_unverified_context") + else None + ) + with tempfile.NamedTemporaryFile(suffix=".py") as f: + print("downloading %s into %s" % (URL, f.name)) + kwargs = dict(context=ssl_context) if ssl_context else {} + req = urlopen(URL, **kwargs) + data = req.read() + req.close() + + f.write(data) + f.flush() + print("download finished, installing pip") + + code = os.system("%s %s --user --upgrade" % (sys.executable, f.name)) + + sys.exit(code) + + +if __name__ == "__main__": + main() diff --git a/scripts/internal/winmake.py b/scripts/internal/winmake.py index 20126ba7c..3995a46f2 100755 --- a/scripts/internal/winmake.py +++ b/scripts/internal/winmake.py @@ -29,31 +29,25 @@ APPVEYOR = bool(os.environ.get('APPVEYOR')) PYTHON = sys.executable if APPVEYOR else os.getenv('PYTHON', sys.executable) -RUNNER_PY = 'psutil\\tests\\runner.py' -GET_PIP_URL = "https://bootstrap.pypa.io/get-pip.py" PY3 = sys.version_info[0] >= 3 +PYTEST_ARGS = "-v -s --tb=short" +if PY3: + PYTEST_ARGS += "-o " HERE = os.path.abspath(os.path.dirname(__file__)) ROOT_DIR = os.path.realpath(os.path.join(HERE, "..", "..")) PYPY = '__pypy__' in sys.builtin_module_names -DEPS = [ - "coverage", - "pdbpp", - "pip", - "pyperf", - "pyreadline", - "requests", - "setuptools", - "wheel", -] - -if sys.version_info[0] < 3: - DEPS.append('mock') - DEPS.append('ipaddress') - DEPS.append('enum34') - -if not PYPY: - DEPS.append("pywin32") - DEPS.append("wmi") +WINDOWS = os.name == "nt" +if PY3: + GET_PIP_URL = "https://bootstrap.pypa.io/get-pip.py" +else: + GET_PIP_URL = "https://bootstrap.pypa.io/pip/2.7/get-pip.py" + +sys.path.insert(0, ROOT_DIR) # so that we can import setup.py + +import setup # NOQA + +TEST_DEPS = setup.TEST_DEPS +DEV_DEPS = setup.DEV_DEPS _cmds = {} if PY3: @@ -101,6 +95,8 @@ def stderr_handle(): def win_colorprint(s, color=LIGHTBLUE): + if not WINDOWS: + return print(s) color += 8 # bold handle = stderr_handle() SetConsoleTextAttribute = ctypes.windll.Kernel32.SetConsoleTextAttribute @@ -368,24 +364,31 @@ def clean(): safe_rmtree("tmp") -def setup_dev_env(): +def install_pydeps_test(): + """Install useful deps.""" + install_pip() + install_git_hooks() + sh("%s -m pip install -U %s" % (PYTHON, " ".join(TEST_DEPS))) + + +def install_pydeps_dev(): """Install useful deps.""" install_pip() install_git_hooks() - sh("%s -m pip install -U %s" % (PYTHON, " ".join(DEPS))) + sh("%s -m pip install -U %s" % (PYTHON, " ".join(DEV_DEPS))) -def test(name=RUNNER_PY): +def test(args=""): """Run tests.""" build() - sh("%s %s" % (PYTHON, name)) + sh("%s -m pytest %s %s" % (PYTHON, PYTEST_ARGS, args)) def coverage(): """Run coverage tests.""" # Note: coverage options are controlled by .coveragerc file build() - sh("%s -m coverage run %s" % (PYTHON, RUNNER_PY)) + sh("%s -m coverage run -m pytest %s" % (PYTHON, PYTEST_ARGS)) sh("%s -m coverage report" % PYTHON) sh("%s -m coverage html" % PYTHON) sh("%s -m webbrowser -t htmlcov/index.html" % PYTHON) @@ -448,13 +451,13 @@ def test_testutils(): def test_by_name(name): """Run test by name.""" build() - sh("%s -m unittest -v %s" % (PYTHON, name)) + test(name) def test_last_failed(): """Re-run tests which failed on last run.""" build() - sh("%s %s --last-failed" % (PYTHON, RUNNER_PY)) + test("--last-failed") def test_memleaks(): @@ -499,6 +502,14 @@ def print_api_speed(): sh("%s -Wa scripts\\internal\\print_api_speed.py" % PYTHON) +def print_sysinfo(): + """Print system info.""" + build() + from psutil.tests import print_sysinfo + + print_sysinfo() + + def download_appveyor_wheels(): """Download appveyor wheels.""" sh( @@ -555,9 +566,11 @@ def parse_args(): sp.add_parser('install', help="build + install in develop/edit mode") sp.add_parser('install-git-hooks', help="install GIT pre-commit hook") sp.add_parser('install-pip', help="install pip") + sp.add_parser('install-pydeps-dev', help="install dev python deps") + sp.add_parser('install-pydeps-test', help="install python test deps") sp.add_parser('print-access-denied', help="print AD exceptions") sp.add_parser('print-api-speed', help="benchmark all API calls") - sp.add_parser('setup-dev-env', help="install deps") + sp.add_parser('print-sysinfo', help="print system info") test = sp.add_parser('test', help="[ARG] run tests") test_by_name = sp.add_parser('test-by-name', help=" run test by name") sp.add_parser('test-connections', help="run connections tests") diff --git a/setup.py b/setup.py index e3375004b..949716a5b 100755 --- a/setup.py +++ b/setup.py @@ -31,15 +31,11 @@ from setuptools import Extension from setuptools import setup except ImportError: + if "CIBUILDWHEEL" in os.environ: + raise setuptools = None from distutils.core import Extension from distutils.core import setup - try: - from wheel.bdist_wheel import bdist_wheel - except ImportError: - if "CIBUILDWHEEL" in os.environ: - raise - bdist_wheel = None HERE = os.path.abspath(os.path.dirname(__file__)) @@ -68,6 +64,53 @@ CP37_PLUS = PY37_PLUS and sys.implementation.name == "cpython" Py_GIL_DISABLED = sysconfig.get_config_var("Py_GIL_DISABLED") +# Test deps, installable via `pip install .[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", + ] +if WINDOWS and not PYPY: + TEST_DEPS.append("pywin32") + TEST_DEPS.append("wheel") + TEST_DEPS.append("wmi") + +# Development deps, installable via `pip install .[dev]`. +DEV_DEPS = [ + "black", + "check-manifest", + "coverage", + "packaging", + "pylint", + "pyperf", + "pypinfo", + "pytest-cov", + "requests", + "rstcheck", + "ruff", + "sphinx", + "sphinx_rtd_theme", + "toml-sort", + "twine", + "virtualenv", + "wheel", +] +if WINDOWS: + DEV_DEPS.append("pyreadline") + DEV_DEPS.append("pdbpp") + macros = [] if POSIX: macros.append(("PSUTIL_POSIX", 1)) @@ -88,19 +131,6 @@ sources.append('psutil/_psutil_posix.c') -extras_require = { - "test": [ - "enum34; python_version <= '3.4'", - "ipaddress; python_version < '3.0'", - "mock; python_version < '3.0'", - ] -} -if not PYPY: - extras_require['test'].extend( - ["pywin32; sys.platform == 'win32'", "wmi; sys.platform == 'win32'"] - ) - - def get_version(): INIT = os.path.join(HERE, 'psutil/__init__.py') with open(INIT) as f: @@ -120,16 +150,19 @@ def get_version(): # Py_LIMITED_API lets us create a single wheel which works with multiple # python versions, including unreleased ones. -if bdist_wheel and CP36_PLUS and (MACOS or LINUX) and not Py_GIL_DISABLED: +if setuptools and CP36_PLUS and (MACOS or LINUX) and not Py_GIL_DISABLED: py_limited_api = {"py_limited_api": True} + options = {"bdist_wheel": {"py_limited_api": "cp36"}} macros.append(('Py_LIMITED_API', '0x03060000')) -elif bdist_wheel and CP37_PLUS and WINDOWS and not Py_GIL_DISABLED: +elif setuptools and CP37_PLUS and WINDOWS and not Py_GIL_DISABLED: # PyErr_SetFromWindowsErr / PyErr_SetFromWindowsErrWithFilename are # part of the stable API/ABI starting with CPython 3.7 py_limited_api = {"py_limited_api": True} + options = {"bdist_wheel": {"py_limited_api": "cp37"}} macros.append(('Py_LIMITED_API', '0x03070000')) else: py_limited_api = {} + options = {} def get_long_description(): @@ -225,6 +258,9 @@ def get_winver(): ('PSAPI_VERSION', 1), ]) + if Py_GIL_DISABLED: + macros.append(('Py_GIL_DISABLED', 1)) + ext = Extension( 'psutil._psutil_windows', sources=( @@ -426,22 +462,11 @@ def get_sunos_update(): else: extensions = [ext] -cmdclass = {} -if py_limited_api: - - class bdist_wheel_abi3(bdist_wheel): - def get_tag(self): - python, _abi, plat = bdist_wheel.get_tag(self) - return python, "abi3", plat - - cmdclass["bdist_wheel"] = bdist_wheel_abi3 - def main(): kwargs = dict( name='psutil', version=VERSION, - cmdclass=cmdclass, description=__doc__.replace('\n', ' ').strip() if __doc__ else '', long_description=get_long_description(), long_description_content_type='text/x-rst', @@ -461,6 +486,7 @@ def main(): license='BSD-3-Clause', packages=['psutil', 'psutil.tests'], ext_modules=extensions, + options=options, classifiers=[ 'Development Status :: 5 - Production/Stable', 'Environment :: Console', @@ -509,6 +535,10 @@ def main(): ], ) if setuptools is not None: + extras_require = { + "dev": DEV_DEPS, + "test": TEST_DEPS, + } kwargs.update( python_requires=( ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"