diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 7e0bc4a8..90403a09 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -28,12 +28,12 @@ jobs: fail-fast: false matrix: include: - - python-version: '3.6' - os: ubuntu-20.04 - - python-version: '3.10' - os: ubuntu-22.04 - python-version: '3.11' os: ubuntu-22.04 + - python-version: '3.12' + os: ubuntu-22.04 + - python-version: '3.13' + os: ubuntu-22.04 runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 @@ -58,21 +58,6 @@ jobs: pip install 'virtualenv<20.22' 'tox==4.5.1' tox-gh-actions tox --workdir .github/workflows/.tox --recreate - # tox >= 4.0.0 is needed for using optional-dependencies from pyproject.toml, which is - # is not available for python <= 3.6, so use the python3.8 of Ubuntu-20.04 to install it: - - name: Run tox for 3.6 and 3.8 on ${{ matrix.os }}'s 3.8 to get 'extras' from pyproject.toml) - if: ${{ matrix.python-version == 2.7 || matrix.python-version == 3.6 }} - run: | - set -xv;curl -sSL https://bootstrap.pypa.io/get-pip.py -o get-pip.py - python3.8 get-pip.py - # The alternative is installing python3-pip but we don't need full pip function for now: - # sudo apt-get update && sudo apt-get install -y python3-pip - # Let tox-gh-actions get the environment(s) to run tests with from tox.ini: - # Use tox==4.5.1: tox>=4 is needed for reading the extras from pyproject.toml - # Warning: tox>=4.5.2 depends on virutalenv>=20.23, which breaks Python 2.7: - python3.8 -m pip install 'virtualenv<20.22' 'tox==4.5.1' tox-gh-actions - tox --workdir .github/workflows/.tox --recreate - - name: Select the coverage file for upload if: | ( matrix.python-version == '3.6' || matrix.python-version == '3.11' ) && diff --git a/.pylintrc b/.pylintrc index dc41d223..5b5816c0 100644 --- a/.pylintrc +++ b/.pylintrc @@ -65,6 +65,7 @@ disable=W0142,W0703,C0111,R0201,W0603,W0613,W0212,W0141, len-as-condition, no-else-return, raise-missing-from, + too-many-positional-arguments, too-many-branches, too-many-nested-blocks, too-many-statements, @@ -222,8 +223,9 @@ defining-attr-methods=__init__,__new__,setUp [DESIGN] -# Maximum number of arguments for function / method -max-args=100 +# Maximum number of arguments for function / method. +# defaults to: max-args=5 +max-args=10 # Argument names that match this expression will be ignored. Default to name # with leading underscore diff --git a/pyproject.toml b/pyproject.toml index 474a6e80..f6ccb075 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -71,11 +71,6 @@ mypy = [ "types-six", "types-toml", ] -# pyre introduced two false-postives recently, pin it to prevent further surprises: -pyre = [ - "pyre-check == 0.9.21", - "pyre-extensions == 0.0.30", -] pytype = [ "pandas", "pytype", @@ -198,7 +193,7 @@ exclude = [ extraPaths = ["stubs"] include = ["xcp", "tests"] pythonPlatform = "Linux" -pythonVersion = "3.6" +pythonVersion = "3.11" reportFunctionMemberAccess = true reportGeneralTypeIssues = "warning" reportOptionalMemberAccess = "warning" @@ -211,7 +206,7 @@ stubPath = "stubs" inputs = ["xcp", "tests", "./*.py"] keepgoing = true platform = "linux" -python_version = "3.10" +python_version = "3.11" pythonpath = ".:stubs" disable = ["ignored-type-comment"] overriding_parameter_count_checks = true diff --git a/pyre_runner.py b/pyre_runner.py deleted file mode 100755 index a67d2427..00000000 --- a/pyre_runner.py +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env python -""" -Run a one-time pyre static analysis check without needing a .pyre_configuration -Gets the paths dynamically so it can be used in tox and GitHub CI -""" -import os -import sys -import time - -import mock - -me = os.path.basename(__file__) + ":" - -pyre_typesched = os.environ.get("PYRE_TYPESHED", None) -if pyre_typesched and os.path.exists(pyre_typesched + "/stdlib/os/path.pyi"): - print("Using {env:PYRE_TYPESHED}:", pyre_typesched) -else: - pyre_typesched = sys.path[-1] + "/mypy/typeshed" - if os.path.exists(pyre_typesched + "/stdlib/os/path.pyi"): - print("Using python_lib:", pyre_typesched) - else: - pyre_typesched = "/tmp/typeshed" - if os.path.exists(pyre_typesched + "/stdlib/os/path.pyi"): - print("Using:", pyre_typesched) - else: - clone = "git clone --depth 1 https://github.com/python/typeshed " - print(me, "Falling back to:", clone + pyre_typesched) - ret = os.system(clone + pyre_typesched) - if ret or not os.path.exists(pyre_typesched + "/stdlib/os/path.pyi"): - print(me, "Could not find or clone typeshed, giving up.") - sys.exit(0) - -command = ( - "pyre", - "--source-directory", - "xcp", - "--source-directory", - "tests", - "--search-path", - "stubs", - "--search-path", - ".", - "--search-path", - os.path.dirname(mock.__file__), - "--typeshed", - pyre_typesched, - "check", -) -cmd = " ".join(command) -print(me, "Running:", cmd) -start_time = time.time() -ret = os.system(cmd) -duration = time.time() - start_time -r = os.waitstatus_to_exitcode(ret) # type: ignore[module-addr] # newer versions have it -if r == 0: - print(me, f"OK pyre took: {duration:.1f}s") -else: - print(me, "Ran:", cmd) - print(me, "exit code:", r) - if os.environ.get("ACT", None): - time.sleep(10) -sys.exit(r) diff --git a/pytest.ini b/pytest.ini index e07456ee..4a1c8466 100644 --- a/pytest.ini +++ b/pytest.ini @@ -11,7 +11,6 @@ required_plugins = pytest_httpserver pytest-forked - pytest-localftpserver pytest-pythonpath pytest-subprocess pytest-timeout @@ -38,4 +37,5 @@ filterwarnings=ignore:Unknown config option pythonpath=stubs # Disable when using pytest >= 7.0.0: # Used by pytest-pythonpath for Python >=2.7 (https://pypi.org/project/pytest-pythonpath): -python_paths=stubs +# Can now only be activated when using newer pytest: +#python_paths=stubs diff --git a/stubs/pyfakefs/__init__.pyi b/stubs/pyfakefs/__init__.pyi deleted file mode 100644 index e69de29b..00000000 diff --git a/stubs/pyfakefs/fake_filesystem_unittest.pyi b/stubs/pyfakefs/fake_filesystem_unittest.pyi deleted file mode 100644 index aade18b0..00000000 --- a/stubs/pyfakefs/fake_filesystem_unittest.pyi +++ /dev/null @@ -1,20 +0,0 @@ -from types import ModuleType -from typing import Any, Callable, Dict, List, Optional, Union - -from _typeshed import Incomplete # pylint: disable=import-error - -Patcher = Incomplete -PatchMode = Incomplete - -def patchfs( - _func: Optional[Incomplete] = ..., - *, - additional_skip_names: Optional[List[Union[str, ModuleType]]] = ..., - modules_to_reload: Optional[List[ModuleType]] = ..., - modules_to_patch: Optional[Dict[str, ModuleType]] = ..., - allow_root_user: bool = ..., - use_known_patches: bool = ..., - patch_open_code: PatchMode = ..., - patch_default_args: bool = ..., - use_cache: bool = ..., -) -> Callable[[Any], Any]: ... diff --git a/stubs/pytest.pyi b/stubs/pytest.pyi deleted file mode 100644 index 6e31256b..00000000 --- a/stubs/pytest.pyi +++ /dev/null @@ -1,8 +0,0 @@ -# pylint: disable=reimported,no-name-in-module,unused-import,function-redefined.redefined-builtin -from _pytest.python_api import raises -from _typeshed import Incomplete as fixture -from _typeshed import Incomplete as mark - -def skip(msg: str = "", *, allow_module_level: bool = False): ... - -__all__ = ["mark", "fixture", "skip", "raises"] diff --git a/stubs/werkzeug/wrappers.pyi b/stubs/werkzeug/wrappers.pyi deleted file mode 100644 index f4a46d17..00000000 --- a/stubs/werkzeug/wrappers.pyi +++ /dev/null @@ -1,3 +0,0 @@ -from _typeshed import Incomplete # pylint: disable=import-error,no-name-in-module -Request = Incomplete -Response = Incomplete diff --git a/tests/conftest.py b/tests/conftest.py index 1a2a4974..68cc4fd5 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -5,10 +5,20 @@ This module is run automatically by pytest to define and enable fixtures. """ -# pyre-ignore-all-errors[21] +import os +import tempfile import warnings -import pytest # pyre does not find the module when run by tox -e py311-pyre +import pytest + + +@pytest.fixture() +def mount_dir(): + """pytest fixture for getting a temporary name for a temp directory""" + mount_point = tempfile.mkdtemp(prefix="media-", dir="/tmp") + os.rmdir(mount_point) + return mount_point.encode() + @pytest.fixture(autouse=True) def set_warnings(): diff --git a/tests/httpserver_testcase.py b/tests/httpserver_testcase.py index b9dc5ceb..cad59524 100644 --- a/tests/httpserver_testcase.py +++ b/tests/httpserver_testcase.py @@ -1,4 +1,5 @@ import os +import sys import unittest from typing import Callable, Optional @@ -11,9 +12,10 @@ from pytest_httpserver import HTTPServer from werkzeug.wrappers import Request, Response - ErrorHandler = Optional[Callable[[Request], Response]] + ErrorHandler = Optional[Callable[[Request], Response | None]] except ImportError: pytest.skip(allow_module_level=True) + sys.exit(0) # Let pyright know that this is a dead end class HTTPServerTestCase(unittest.TestCase): @@ -31,7 +33,7 @@ def tearDownClass(cls): @classmethod def serve_file(cls, root, file_path, error_handler=None, real_path=None): - # type:(str, str, Optional[Callable[[Request], Response]], Optional[str]) -> None + # type:(str, str, ErrorHandler, Optional[str]) -> None """Expect a GET request and handle it using the local pytest_httpserver.HTTPServer""" def handle_get(request): diff --git a/tests/test_accessor.py b/tests/test_accessor.py index 93f5d609..ebd1ab70 100644 --- a/tests/test_accessor.py +++ b/tests/test_accessor.py @@ -1,24 +1,27 @@ import unittest - -from pyfakefs.fake_filesystem import FakeFilesystem +from typing import TYPE_CHECKING import xcp.accessor from .test_mountingaccessor import check_binary_read, check_binary_write +if TYPE_CHECKING: + import pyfakefs + import pytest + -def test_file_accessor(fs): - # type:(FakeFilesystem) -> None +def test_file_accessor(fs, caplog): + # type(pyfakefs.fake_filesystem.FakeFilesystem, pytest.LogCaptureFixture) -> None """Test FileAccessor.writeFile(), .openAddress and .access using pyfakefs""" accessor = xcp.accessor.createAccessor("file://repo/", False) assert isinstance(accessor, xcp.accessor.FileAccessor) check_binary_read(accessor, "/repo", fs) - check_binary_write(accessor, "/repo", fs) + check_binary_write(accessor, "/repo", fs, caplog) class TestAccessor(unittest.TestCase): def setUp(self): - """Provide the refrence content of the repo/.treeinfo file for check_repo_access()""" + """Provide the reference content of the repo/.treeinfo file for check_repo_access()""" with open("tests/data/repo/.treeinfo", "rb") as dot_treeinfo: self.reference_treeinfo = dot_treeinfo.read() diff --git a/tests/test_bootloader.py b/tests/test_bootloader.py index 82500336..23b21a5e 100644 --- a/tests/test_bootloader.py +++ b/tests/test_bootloader.py @@ -41,8 +41,7 @@ def test_grub2(self): universal_newlines=True) assert proc.stdout for line in proc.stdout: - # pylint: disable-next=deprecated-method - self.assertRegexpMatches(line, r"^(5a6,13$|>)") + self.assertRegex(line, r"^(5a6,13$|>)") # Replaced removed assert call proc.stdout.close() proc.wait() diff --git a/tests/test_ftpaccessor.py b/tests/test_ftpaccessor.py index a5b0b303..340b759c 100644 --- a/tests/test_ftpaccessor.py +++ b/tests/test_ftpaccessor.py @@ -21,13 +21,14 @@ from io import BytesIO import pytest -import pytest_localftpserver # pylint: disable=unused-import # Ensure that it is installed +import pytest_localftpserver # Ensure that it is installed from six import ensure_binary, ensure_str import xcp.accessor binary_data = b"\x80\x91\xaa\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xcc\xdd\xee\xff" text_data = "✋➔Hello Accessor from the 🗺, download and verify ✅ me!" +assert pytest_localftpserver def upload_textfile(ftpserver, accessor): diff --git a/tests/test_httpaccessor.py b/tests/test_httpaccessor.py index d0248f7f..c6bd38fa 100644 --- a/tests/test_httpaccessor.py +++ b/tests/test_httpaccessor.py @@ -43,7 +43,6 @@ def http_get_request_data(self, url, read_file, error_handler): def assert_http_get_request_data(self, url, read_file, error_handler): # type:(str, str, ErrorHandler) -> HTTPAccessor - # pyre-ignore[23]: silence false positive with self.http_get_request_data(url, read_file, error_handler) as (httpaccessor, ref): http_accessor_filehandle = httpaccessor.openAddress(read_file) if sys.version_info >= (3, 0): diff --git a/tests/test_ifrename_logic.py b/tests/test_ifrename_logic.py index 5d445f3c..f6966054 100644 --- a/tests/test_ifrename_logic.py +++ b/tests/test_ifrename_logic.py @@ -1,4 +1,4 @@ -# pyright: reportGeneralTypeIssues=false +# pyright: reportGeneralTypeIssues=false,reportAttributeAccessIssue=false # pytype: disable=attribute-error from __future__ import print_function from __future__ import unicode_literals diff --git a/tests/test_logger.py b/tests/test_logger.py index af8cd5c3..fb6b5a32 100644 --- a/tests/test_logger.py +++ b/tests/test_logger.py @@ -10,7 +10,8 @@ from xcp.logger import openLog -def test_openLog_mock_open(): +def test_openLog_mock_open(fs): + # type(FakeFilesystem) -> None """Cover xcp.logger.openLog.open_with_codec_handling and check the arguments used for open()""" fh = StringIO() with patch("xcp.compat.open", mock_open()) as open_mock: @@ -31,3 +32,4 @@ def test_openLog_mock_stdin(): assert openLog("test.log") is True os.close(slave_fd) os.close(master_fd) + open_mock.assert_called_once_with("test.log", "a", **open_utf8) diff --git a/tests/test_mountingaccessor.py b/tests/test_mountingaccessor.py index 9dea2d3f..5bc41f5c 100644 --- a/tests/test_mountingaccessor.py +++ b/tests/test_mountingaccessor.py @@ -1,8 +1,13 @@ """pytest tests testing subclasses of xcp.accessor.MountingAccessor using pyfakefs""" + +import logging +import os import sys from io import BytesIO from typing import TYPE_CHECKING, cast +import pytest +from pytest import LogCaptureFixture as LogCap from mock import patch from pyfakefs.fake_filesystem import FakeFileOpen, FakeFilesystem @@ -17,9 +22,8 @@ if TYPE_CHECKING: from typing_extensions import Literal else: - import pytest - pytest.skip(allow_module_level=True) + sys.exit(0) # Let pyright know that this is a dead end binary_data = b"\x00\x1b\x5b\x95\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xcc\xdd\xee\xff" @@ -28,8 +32,8 @@ def expect(fp, mount): fp.register_subprocess(mount) # type: ignore[arg-type] -def test_device_accessor(fs, fp): - # type: (FakeFilesystem, FakeProcess) -> None +def test_device_accessor(fs, fp, mount_dir, caplog): + # type: (FakeFilesystem, FakeProcess, bytes, LogCap) -> None assert isinstance(fp, FakeProcess) # Test xcp.mount.bindMount() @@ -37,15 +41,15 @@ def test_device_accessor(fs, fp): fp.register_subprocess(mount) # type: ignore[arg-type] assert xcp.mount.bindMount("src", "mountpoint_dest") is None - expect(fp, [b"/bin/mount", b"-t", b"iso9660", b"-o", b"ro", b"/dev/device", b"/tmp"]) + expect(fp, [b"/bin/mount", b"-t", b"iso9660", b"-o", b"ro", b"/dev/device", mount_dir]) accessor = xcp.accessor.createAccessor("dev:///dev/device", False) assert isinstance(accessor, xcp.accessor.MountingAccessorTypes) - check_mounting_accessor(accessor, fs, fp) + check_mounting_accessor(accessor, fs, fp, mount_dir, caplog) -def test_nfs_accessor(fs, fp): - # type: (FakeFilesystem, FakeProcess) -> None +def test_nfs_accessor(fs, fp, mount_dir, caplog): + # type: (FakeFilesystem, FakeProcess, bytes, LogCap) -> None assert isinstance(fp, FakeProcess) mount = [ b"/bin/mount", @@ -54,21 +58,22 @@ def test_nfs_accessor(fs, fp): b"-o", b"tcp,timeo=100,retrans=1,retry=0", b"server/path", - b"/tmp", + mount_dir, ] expect(fp, mount) accessor = xcp.accessor.createAccessor("nfs://server/path", False) assert isinstance(accessor, xcp.accessor.NFSAccessor) - check_mounting_accessor(accessor, fs, fp) + check_mounting_accessor(accessor, fs, fp, mount_dir, caplog) -def check_mounting_accessor(accessor, fs, fp): - # type: (Literal[False] | xcp.accessor.Mount, FakeFilesystem, FakeProcess) -> None +def check_mounting_accessor(accessor, fs, fp, mount_point, caplog): + # type: (xcp.accessor.Mount, FakeFilesystem, FakeProcess, bytes, LogCap) -> None """Test subclasses of MountingAccessor (with xcp.cmd.runCmd in xcp.mount mocked)""" assert isinstance(accessor, xcp.accessor.MountingAccessorTypes) with patch("tempfile.mkdtemp") as tempfile_mkdtemp: - tempfile_mkdtemp.return_value = "/tmp" + os.mkdir(mount_point) + tempfile_mkdtemp.return_value = mount_point.decode() accessor.start() assert accessor.location @@ -80,13 +85,13 @@ def check_mounting_accessor(accessor, fs, fp): fs.add_mount_point(location) assert check_binary_read(accessor, location, fs) - assert check_binary_write(accessor, location, fs) + assert check_binary_write(accessor, location, fs, caplog) assert open_text(accessor, location, fs, UTF8TEXT_LITERAL) == UTF8TEXT_LITERAL if sys.version_info.major >= 3: fs.mount_points.pop(location) - umount = [b"/bin/umount", b"-d", b"/tmp"] + umount = [b"/bin/umount", b"-d", mount_point] fp.register_subprocess(umount) # type: ignore[arg-type] accessor.finish() @@ -116,15 +121,20 @@ def check_binary_read(accessor, location, fs): return cast(bytes, binary_file.read()) == binary_data -def check_binary_write(accessor, location, fs): - # type: (Literal[False] | xcp.accessor.AnyAccessor, str, FakeFilesystem) -> bool +def check_binary_write(accessor, location, fs, caplog): + # type: (xcp.accessor.AnyAccessor, str, FakeFilesystem, LogCap) -> bool """Test the writeFile() method of different types of local Accessor classes""" assert isinstance(accessor, xcp.accessor.LocalTypes) name = "binary_file_written_by_accessor" - accessor.writeFile(BytesIO(binary_data), name) - - assert accessor.access(name) + with caplog.at_level(logging.FATAL): + accessor.writeFile(BytesIO(binary_data), name) + if caplog.record_tuples: + logger, level, message = caplog.record_tuples[0] + assert logger == "root" + assert level == logging.FATAL + assert message == "Copying to " + name + assert accessor.access(name) with FakeFileOpen(fs, delete_on_close=True)(location + "/" + name, "rb") as written: return cast(bytes, written.read()) == binary_data diff --git a/tests/test_pci.py b/tests/test_pci.py index 4941a0a6..eb7bf124 100644 --- a/tests/test_pci.py +++ b/tests/test_pci.py @@ -11,6 +11,8 @@ if sys.version_info >= (3, 6): from pytest_subprocess.fake_process import FakeProcess +else: + pytest.skip(allow_module_level=True) class TestInvalid(unittest.TestCase): @@ -179,6 +181,7 @@ def test_videoclass_by_mock_calls(fp): def mock_lspci_using_open_testfile(fp): """Mock xcp.pci.PCIDevices.Popen() using open(tests/data/lspci-mn)""" with open("tests/data/lspci-mn", "rb") as fake_data: - assert isinstance(fp, FakeProcess) + if sys.version_info >= (3, 6): + assert isinstance(fp, FakeProcess) fp.register_subprocess(["lspci", "-mn"], stdout=fake_data.read()) return PCIDevices() diff --git a/tox.ini b/tox.ini index d7386335..fbbefac5 100644 --- a/tox.ini +++ b/tox.ini @@ -1,11 +1,11 @@ [tox] # This is the order how tox runs the tests when used interactively during development. # Run the tests which uncover issues most often first! For example: -# 1. python 2.7 and 3.10 coverage test for changed, but not covered lines and mypy check +# 1. python 3.11 coveragest for changed, but not covered lines and mypy check # 2. python 3.6 test and pylint warnings from changed lines -# 3. pytype (needs Python 3.8 for best results) -# 4. pyre and pyright checks, pytest test report as markdown for GitHub Actions summary -envlist = py38-covcombine-check, py36-lint-test, py310-pytype, py311-pyre-mdreport +# 3. pytype (needs Python 3.8 or newer for best results) +# 4. pyright checks, pytest test report as markdown for GitHub Actions summary +envlist = py311-covcp-check-mdreport, py312-cov-pytype, py313-cov-lint-pyright isolated_build = true skip_missing_interpreters = true requires = @@ -28,9 +28,9 @@ commands = # https://github.com/actions/toolkit/blob/main/docs/commands.md#problem-matchers echo "::add-matcher::.github/workflows/PYTHONWARNINGS-problemMatcher.json" pytest --cov -v --new-first -x --show-capture=all -rA - sh -c 'ls -l {env:COVERAGE_FILE}' sh -c 'if [ -n "{env:PYTEST_MD_REPORT_OUTPUT}" -a -n "{env:GITHUB_STEP_SUMMARY}" ];then \ - sed -i "s/tests\(.*py\)/[&](&)/" {env:PYTEST_MD_REPORT_OUTPUT}; sed "/title/,/\/style/d" \ + mkdir -p $(dirname "{env:GITHUB_STEP_SUMMARY:.git/sum.md}"); \ + sed "s/tests\(.*py\)/[&](&)/" \ {env:PYTEST_MD_REPORT_OUTPUT} >{env:GITHUB_STEP_SUMMARY:.git/sum.md};fi' [testenv] @@ -42,14 +42,12 @@ description = Run in a {basepython} virtualenv: lint: {[lint]description} mdreport: Make a test report (which is shown in the GitHub Actions Summary Page) test: {[test]description} - # https://pypi.org/project/pyre-check/ pyre intro: https://youtu.be/0FSXS5kw2m4 - pyre: Run pyre for static analyis, only passes using: tox -e py311-pyre check: Run mypy for static analyis pytype: Run pytype for static analyis, intro: https://youtu.be/abvW0mOrDiY # checkers(mypy) need the pytest dependices as well: extras = {check,pytype}: {[check]extras} - {cov,covcp,covcombine,fox,check,lint,test,pytype,pyre,mdreport}: {[test]extras} + {cov,covcp,covcombine,fox,check,lint,test,pytype,pyright,mdreport}: {[test]extras} {cov,covcp,covcombine,fox}: {[cov]extras} deps = mdreport: pytest-md-report @@ -58,9 +56,7 @@ deps = {cov,covcp,covcombine,fox}: coverage[toml] {cov,covcp,covcombine,fox}: diff-cover {lint,fox}: {[lint]deps} - pyre: pyre-check - pyre: pyre-extensions - pyre: pyright + pyright: pyright pytype: {[pytype]deps} allowlist_externals = {cov,covcp,covcombine,fox,check,lint,test,mdreport}: echo @@ -81,7 +77,6 @@ passenv = covcp: HOME check: MYPY_FORCE_COLOR check: MYPY_FORCE_TERMINAL_WIDTH - pyre: PYRE_TYPESHED {fox,check,pytype}: TERM fox: DISPLAY fox: XAUTHORITY @@ -104,7 +99,7 @@ setenv = {[cov]setenv} commands = lint: {[lint]commands} - pyre: {[pyre]commands} + pyright: {[pyright]commands} check: {[check]commands} pytype: {[pytype]commands} {cov,covcp,covcombine,check,fox,test,mdreport}: {[test]commands} @@ -126,7 +121,7 @@ extras = coverage commands = coverage xml -o {envlogdir}/coverage.xml --fail-under {env:XCP_COV_MIN:68} coverage html -d {envlogdir}/htmlcov - coverage html -d {envlogdir}/htmlcov-tests --fail-under {env:TESTS_COV_MIN:96} \ + coverage html -d {envlogdir}/htmlcov-tests --fail-under {env:TESTS_COV_MIN:95} \ --include="tests/*" diff-cover --compare-branch=origin/master --include-untracked \ {env:PY3_DIFFCOVER_OPTIONS} --fail-under {env:DIFF_COV_MIN:92} \ @@ -189,6 +184,8 @@ python = 3.9: py39 3.10: py310 3.11: py311 + 3.12: py312 + 3.13: py313 [check] extras = mypy @@ -200,17 +197,16 @@ commands = ignore = W191,W293,W504,E101,E126,E127,E201,E202,E203,E221,E222,E226,E227,E241,E251,E261,E262,E265,E301,E302,E303,E305,E722,W391,E401,E402,E741 max-line-length = 129 -[pyre] +[pyright] commands = - pyre: python3.11 --version -V # Needs py311-pyre, does not work with py310-pyre - python pyre_runner.py - -pyright + pyright [pytype] deps = pytype pandas commands = - python3.10 -V # Needs python <= 3.10, and 3.10 is needed to parse new "|" syntax + python3 --version + # python3.11 -V # Needs python <= 3.10, and 3.10 is needed to parse new "|" syntax pytype --version # Runs pytype -j auto -k --config .github/workflows/pytype.cfg and parses the output: python3 pytype_runner.py # When switching versions, update .github/workflows/pytype.cfg too! diff --git a/xcp/accessor.py b/xcp/accessor.py index 9583dfe7..0ceec1c1 100644 --- a/xcp/accessor.py +++ b/xcp/accessor.py @@ -315,7 +315,8 @@ def finish(self): self.cleanup = False self.ftp = None - def access(self, path): # pylint: disable=arguments-differ,arguments-renamed + # pylint: disable-next=arguments-differ,arguments-renamed + def access(self, path): # pyright: ignore[reportIncompatibleMethodOverride] try: logger.debug("Testing "+path) self._cleanup() diff --git a/xcp/bootloader.py b/xcp/bootloader.py index 353cc7ff..28eea30e 100644 --- a/xcp/bootloader.py +++ b/xcp/bootloader.py @@ -32,7 +32,7 @@ import xcp.cmd -# pyre-ignore-all-errors[21] +# pyre-ignore-all-errors[21,30] try: # xenserver-release.rpm puts a branding.py into our xcp installation directory: from xcp import branding # type:ignore[attr-defined] # pytype: disable=import-error except ImportError: # For CI, use stubs/branding.py (./stubs is added to pythonpath) diff --git a/xcp/logger.py b/xcp/logger.py index 19ed6de6..02995a05 100644 --- a/xcp/logger.py +++ b/xcp/logger.py @@ -64,9 +64,10 @@ def openLog(lfile, level=logging.INFO): h.close() handler = logging.handlers.RotatingFileHandler(lfile, maxBytes=2**31) + # pyre-ignore[16] old = fcntl.fcntl(handler.stream.fileno(), fcntl.F_GETFD) - fcntl.fcntl(handler.stream.fileno(), - fcntl.F_SETFD, old | fcntl.FD_CLOEXEC) + # pyre-ignore[16] + fcntl.fcntl(handler.stream.fileno(), fcntl.F_SETFD, old | fcntl.FD_CLOEXEC) # or if it is not a string, assume its a file-like object else: