diff --git a/pyproject.toml b/pyproject.toml index 35b5a31c0..18bfbfa28 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -193,6 +193,7 @@ disable = [ exclude = [ "docs/conf.py", "psutil/_compat.py", + "psutil/tests/", "scripts/internal/winmake.py", ] ignore_decorators = [ diff --git a/scripts/internal/winmake.py b/scripts/internal/winmake.py index 82a309397..d4ec49ef6 100755 --- a/scripts/internal/winmake.py +++ b/scripts/internal/winmake.py @@ -28,7 +28,7 @@ APPVEYOR = bool(os.environ.get('APPVEYOR')) PYTHON = sys.executable if APPVEYOR else os.getenv('PYTHON', sys.executable) PY3 = sys.version_info[0] >= 3 -PYTEST_ARGS = "-v -s --tb=short" +PYTEST_ARGS = ["-v", "-s", "--tb=short"] HERE = os.path.abspath(os.path.dirname(__file__)) ROOT_DIR = os.path.realpath(os.path.join(HERE, "..", "..")) WINDOWS = os.name == "nt" @@ -100,39 +100,16 @@ def win_colorprint(s, color=LIGHTBLUE): def sh(cmd, nolog=False): + assert isinstance(cmd, list), repr(cmd) if not nolog: - safe_print("cmd: " + cmd) - p = subprocess.Popen( # noqa S602 - cmd, shell=True, env=os.environ, cwd=os.getcwd() + safe_print("cmd: %s" % cmd) + return subprocess.check_output( + cmd, env=os.environ, universal_newlines=True ) - p.communicate() - if p.returncode != 0: - sys.exit(p.returncode) def rm(pattern, directory=False): """Recursively remove a file or dir by pattern.""" - - def safe_remove(path): - try: - os.remove(path) - except OSError as err: - if err.errno != errno.ENOENT: - raise - else: - safe_print("rm %s" % path) - - def safe_rmtree(path): - def onerror(fun, path, excinfo): - exc = excinfo[1] - if exc.errno != errno.ENOENT: - raise # noqa: PLE0704 - - existed = os.path.isdir(path) - shutil.rmtree(path, onerror=onerror) - if existed: - safe_print("rmdir -f %s" % path) - if "*" not in pattern: if directory: safe_rmtree(pattern) @@ -166,14 +143,9 @@ def safe_remove(path): def safe_rmtree(path): - def onerror(fun, path, excinfo): - exc = excinfo[1] - if exc.errno != errno.ENOENT: - raise # noqa: PLE0704 - existed = os.path.isdir(path) - shutil.rmtree(path, onerror=onerror) - if existed: + shutil.rmtree(path, ignore_errors=True) + if existed and not os.path.isdir(path): safe_print("rmdir -f %s" % path) @@ -202,7 +174,7 @@ def build(): """Build / compile.""" # Make sure setuptools is installed (needed for 'develop' / # edit mode). - sh('%s -c "import setuptools"' % PYTHON) + sh([PYTHON, "-c", "import setuptools"]) # "build_ext -i" copies compiled *.pyd files in ./psutil directory in # order to allow "import psutil" when using the interactive interpreter @@ -233,31 +205,31 @@ def build(): p.wait() # Make sure it actually worked. - sh('%s -c "import psutil"' % PYTHON) + sh([PYTHON, "-c", "import psutil"]) win_colorprint("build + import successful", GREEN) def wheel(): """Create wheel file.""" build() - sh("%s setup.py bdist_wheel" % PYTHON) + sh([PYTHON, "setup.py", "bdist_wheel"]) def upload_wheels(): """Upload wheel files on PyPI.""" build() - sh("%s -m twine upload dist/*.whl" % PYTHON) + sh([PYTHON, "-m", "twine", "upload", "dist/*.whl"]) def install_pip(): """Install pip.""" - sh('%s %s' % (PYTHON, os.path.join(HERE, "install_pip.py"))) + sh([PYTHON, os.path.join(HERE, "install_pip.py")]) def install(): """Install in develop / edit mode.""" build() - sh("%s setup.py develop" % PYTHON) + sh([PYTHON, "setup.py", "develop"]) def uninstall(): @@ -278,7 +250,7 @@ def uninstall(): except ImportError: break else: - sh("%s -m pip uninstall -y psutil" % PYTHON) + sh([PYTHON, "-m", "pip", "uninstall", "-y", "psutil"]) finally: os.chdir(here) @@ -336,102 +308,111 @@ def install_pydeps_test(): """Install useful deps.""" install_pip() install_git_hooks() - sh("%s -m pip install --user -U %s" % (PYTHON, " ".join(TEST_DEPS))) + sh([PYTHON, "-m", "pip", "install", "--user", "-U"] + TEST_DEPS) def install_pydeps_dev(): """Install useful deps.""" install_pip() install_git_hooks() - sh("%s -m pip install --user -U %s" % (PYTHON, " ".join(DEV_DEPS))) + sh([PYTHON, "-m", "pip", "install", "--user", "-U"] + DEV_DEPS) -def test(args=""): +def test(args=None): """Run tests.""" + if args: + assert isinstance(args, list), args build() - sh("%s -m pytest %s %s" % (PYTHON, PYTEST_ARGS, args)) + cmd = [PYTHON, "-m", "pytest"] + PYTEST_ARGS + if args: + cmd.extend(args) + sh(cmd) + + +def test_parallel(): + test(["-n", "auto", "--dist", "loadgroup"]) def coverage(): """Run coverage tests.""" # Note: coverage options are controlled by .coveragerc file build() - 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) + sh([PYTHON, "-m", "coverage", "run", "-m", "pytest"] + PYTEST_ARGS) + sh([PYTHON, "-m", "coverage", "report"]) + sh([PYTHON, "-m", "coverage", "html"]) + sh([PYTHON, "-m", "webbrowser", "-t", "htmlcov/index.html"]) def test_process(): """Run process tests.""" build() - sh("%s psutil\\tests\\test_process.py" % PYTHON) + sh([PYTHON, "psutil\\tests\\test_process.py"]) def test_process_all(): """Run process all tests.""" build() - sh("%s psutil\\tests\\test_process_all.py" % PYTHON) + sh([PYTHON, "psutil\\tests\\test_process_all.py"]) def test_system(): """Run system tests.""" build() - sh("%s psutil\\tests\\test_system.py" % PYTHON) + sh([PYTHON, "psutil\\tests\\test_system.py"]) def test_platform(): """Run windows only tests.""" build() - sh("%s psutil\\tests\\test_windows.py" % PYTHON) + sh([PYTHON, "psutil\\tests\\test_windows.py"]) def test_misc(): """Run misc tests.""" build() - sh("%s psutil\\tests\\test_misc.py" % PYTHON) + sh([PYTHON, "psutil\\tests\\test_misc.py"]) def test_unicode(): """Run unicode tests.""" build() - sh("%s psutil\\tests\\test_unicode.py" % PYTHON) + sh([PYTHON, "psutil\\tests\\test_unicode.py"]) def test_connections(): """Run connections tests.""" build() - sh("%s psutil\\tests\\test_connections.py" % PYTHON) + sh([PYTHON, "psutil\\tests\\test_connections.py"]) def test_contracts(): """Run contracts tests.""" build() - sh("%s psutil\\tests\\test_contracts.py" % PYTHON) + sh([PYTHON, "psutil\\tests\\test_contracts.py"]) def test_testutils(): """Run test utilities tests.""" build() - sh("%s psutil\\tests\\test_testutils.py" % PYTHON) + sh([PYTHON, "psutil\\tests\\test_testutils.py"]) def test_by_name(name): """Run test by name.""" build() - test(name) + test([name]) def test_last_failed(): """Re-run tests which failed on last run.""" build() - test("--last-failed") + test(["--last-failed"]) def test_memleaks(): """Run memory leaks tests.""" build() - sh("%s psutil\\tests\\test_memleaks.py" % PYTHON) + sh([PYTHON, "psutil\\tests\\test_memleaks.py"]) def install_git_hooks(): @@ -450,24 +431,24 @@ def install_git_hooks(): def bench_oneshot(): """Benchmarks for oneshot() ctx manager (see #799).""" - sh("%s -Wa scripts\\internal\\bench_oneshot.py" % PYTHON) + sh([PYTHON, "scripts\\internal\\bench_oneshot.py"]) def bench_oneshot_2(): """Same as above but using perf module (supposed to be more precise).""" - sh("%s -Wa scripts\\internal\\bench_oneshot_2.py" % PYTHON) + sh([PYTHON, "scripts\\internal\\bench_oneshot_2.py"]) def print_access_denied(): """Print AD exceptions raised by all Process methods.""" build() - sh("%s -Wa scripts\\internal\\print_access_denied.py" % PYTHON) + sh([PYTHON, "scripts\\internal\\print_access_denied.py"]) def print_api_speed(): """Benchmark all API calls.""" build() - sh("%s -Wa scripts\\internal\\print_api_speed.py" % PYTHON) + sh([PYTHON, "scripts\\internal\\print_api_speed.py"]) def print_sysinfo(): @@ -480,10 +461,14 @@ def print_sysinfo(): def download_appveyor_wheels(): """Download appveyor wheels.""" - sh( - "%s -Wa scripts\\internal\\download_wheels_appveyor.py " - "--user giampaolo --project psutil" % PYTHON - ) + sh([ + PYTHON, + "scripts\\internal\\download_wheels_appveyor.py", + "--user", + "giampaolo", + "--project", + "psutil", + ]) def generate_manifest(): @@ -503,13 +488,9 @@ def get_python(path): path = path.replace('.', '') vers = ( '27', - '27-32', '27-64', - '310-32', '310-64', - '311-32', '311-64', - '312-32', '312-64', ) for v in vers: @@ -539,6 +520,7 @@ def parse_args(): sp.add_parser('print-access-denied', help="print AD exceptions") sp.add_parser('print-api-speed', help="benchmark all API calls") sp.add_parser('print-sysinfo', help="print system info") + sp.add_parser('test-parallel', help="run tests in parallel") 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")