diff --git a/doc/analyze_check_versions.md b/doc/analyze_check_versions.md index 84ea5422d354..daa370cecbc0 100644 --- a/doc/analyze_check_versions.md +++ b/doc/analyze_check_versions.md @@ -5,9 +5,9 @@ This table is to clarify the currently pinned version of tools we run in CI and | Tool | Current Version | Next Version | Next Version Merge Date | |------|-----------------|--------------|-------------------------| -Pylint | 3.2.7 | 3.2.7 | 2026-01-12 | -Pylint Guidelines Checker | 0.5.6 | 0.5.7 | 2026-01-12 | -MyPy | 1.14.1 | 1.18.1 | 2026-01-12 | -Pyright | 1.1.391 | 1.1.405 | 2026-01-12 | +Pylint | 3.2.7 | 4.0.4 | 2026-04-13 | +Pylint Guidelines Checker | 0.5.7 | 0.5.7 | 2026-04-13 | +MyPy | 1.18.1 | 1.19.1 | 2026-04-13 | +Pyright | 1.1.405 | 1.1.407 | 2026-04-13 | Sphinx | 8.2.0 | N/A | N/A | Black | 24.4.0 | N/A | N/A | diff --git a/eng/pylintrc b/eng/pylintrc index a157400a274e..dd3a8b0605b0 100644 --- a/eng/pylintrc +++ b/eng/pylintrc @@ -1,5 +1,6 @@ [MASTER] -ignore-patterns=test_*,conftest,setup +py-version=3.10 +ignore-patterns=conftest,setup reports=no # PYLINT DIRECTORY BLACKLIST. @@ -8,7 +9,7 @@ ignore-paths= azure/mixedreality/remoterendering/_api_version.py, (?:.*[/\\]|^).*[/\\](projects|agents)[/\\](models/_models.py|_model_base.py|operations/_operations.py|aio/operations/_operations.py)$, # Exclude any path that contains the following directory names - (?:.*[/\\]|^)(?:_vendor|_generated|_restclient|samples|examples|test|tests|doc|\.tox)(?:[/\\]|$) + (?:.*[/\\]|^)(?:_vendor|_generated|_restclient|examples|doc|\.tox)(?:[/\\]|$) load-plugins=pylint_guidelines_checker diff --git a/eng/test_pylintrc b/eng/test_pylintrc new file mode 100644 index 000000000000..b95de95de206 --- /dev/null +++ b/eng/test_pylintrc @@ -0,0 +1,66 @@ +[MASTER] +py-version=3.10 +ignore-patterns=conftest,setup +reports=no + +# PYLINT DIRECTORY BLACKLIST. +ignore-paths= + azure\\mixedreality\\remoterendering\\_api_version.py, + azure/mixedreality/remoterendering/_api_version.py, + (?:.*[/\\]|^).*[/\\](projects|agents)[/\\](models/_models.py|_model_base.py|operations/_operations.py|aio/operations/_operations.py)$, + # Exclude any path that contains the following directory names + (?:.*[/\\]|^)(?:_vendor|_generated|_restclient|examples|doc|\.tox)(?:[/\\]|$) + +load-plugins=pylint_guidelines_checker + +[MESSAGES CONTROL] +# For all codes, run 'pylint --list-msgs' or go to 'https://pylint.pycqa.org/en/latest/technical_reference/features.html' +# locally-disabled: Warning locally suppressed using disable-msg +# cyclic-import: because of https://github.com/PyCQA/pylint/issues/850 +# too-many-arguments: Due to the nature of the CLI many commands have large arguments set which reflect in large arguments set in corresponding methods. +# Let's black deal with bad-continuation + +# Added disables from super-with-arguments and client formatting rules for tests +disable=useless-object-inheritance,missing-docstring,locally-disabled,fixme,cyclic-import,too-many-arguments,invalid-name,duplicate-code,too-few-public-methods,consider-using-f-string,super-with-arguments,redefined-builtin,import-outside-toplevel,client-suffix-needed,unnecessary-dunder-call,unnecessary-ellipsis,client-paging-methods-use-list,consider-using-max-builtin,too-many-lines,possibly-used-before-assignment,do-not-hardcode-dedent,arguments-differ,signature-differs,deprecated-class,too-many-positional-arguments,missing-client-constructor-parameter-credential,missing-client-constructor-parameter-kwargs,unapproved-client-method-name-prefix,client-method-has-more-than-5-positional-arguments,client-method-missing-type-annotations,client-method-missing-kwargs,client-method-name-no-double-underscore,client-method-missing-tracing-decorator,client-method-missing-tracing-decorator-async,client-incorrect-naming-convention,client-docstring-use-literal-include,client-lro-methods-use-polling,lro-methods-use-correct-naming,specify-parameter-names-in-call + + +[FORMAT] +max-line-length=120 + +[VARIABLES] +# Tells whether we should check for unused import in __init__ files. +init-import=yes + +[DESIGN] +# Maximum number of locals for function / method body +max-locals=25 +# Maximum number of branch for function / method body +max-branches=20 +# Maximum number of instance attributes for class +max-attributes=10 +# Maximum number of ancestors +max-parents=15 + +[SIMILARITIES] +min-similarity-lines=10 + +[BASIC] +# Naming hints based on PEP 8 (https://www.python.org/dev/peps/pep-0008/#naming-conventions). +# Consider these guidelines and not hard rules. Read PEP 8 for more details. + +# The invalid-name checker must be **enabled** for these hints to be used. +include-naming-hint=yes + +module-naming-style=snake_case +const-naming-style=UPPER_CASE +class-naming-style=PascalCase +class-attribute-naming-style=snake_case +attr-naming-style=snake_case +method-naming-style=snake_case +function-naming-style=snake_case +argument-naming-style=snake_case +variable-naming-style=snake_case +inlinevar-naming-style=snake_case + +[TYPECHECK] +generated-members=js.* \ No newline at end of file diff --git a/eng/tools/azure-sdk-tools/azpysdk/mypy.py b/eng/tools/azure-sdk-tools/azpysdk/mypy.py index d409fca3ebe2..683571bafe61 100644 --- a/eng/tools/azure-sdk-tools/azpysdk/mypy.py +++ b/eng/tools/azure-sdk-tools/azpysdk/mypy.py @@ -15,7 +15,7 @@ from ci_tools.logging import logger PYTHON_VERSION = "3.10" -MYPY_VERSION = "1.14.1" +MYPY_VERSION = "1.18.1" ADDITIONAL_LOCKED_DEPENDENCIES = [ "types-chardet==5.0.4.6", "types-requests==2.31.0.6", diff --git a/eng/tools/azure-sdk-tools/azpysdk/pylint.py b/eng/tools/azure-sdk-tools/azpysdk/pylint.py index 8a571008a567..439a40030f1f 100644 --- a/eng/tools/azure-sdk-tools/azpysdk/pylint.py +++ b/eng/tools/azure-sdk-tools/azpysdk/pylint.py @@ -58,7 +58,7 @@ def run(self, args: argparse.Namespace) -> int: install_into_venv( executable, [ - "azure-pylint-guidelines-checker==0.5.6", + "azure-pylint-guidelines-checker==0.5.7", "--index-url=https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-python/pypi/simple/", ], package_dir, @@ -103,7 +103,10 @@ def run(self, args: argparse.Namespace) -> int: os.path.join(REPO_ROOT, "eng/pylintrc") if args.next else os.path.join(REPO_ROOT, "pylintrc") ) + # Run pylint on main package try: + main_pylint_targets = [os.path.join(package_dir, top_level_module)] + logger.info( [ executable, @@ -111,8 +114,7 @@ def run(self, args: argparse.Namespace) -> int: "pylint", "--rcfile={}".format(rcFileLocation), "--output-format=parseable", - os.path.join(package_dir, top_level_module), - ] + ] + main_pylint_targets ) results.append( @@ -123,22 +125,92 @@ def run(self, args: argparse.Namespace) -> int: "pylint", "--rcfile={}".format(rcFileLocation), "--output-format=parseable", - os.path.join(package_dir, top_level_module), - ] + ] + main_pylint_targets ) ) except CalledProcessError as e: logger.error( - "{} exited with linting error {}. Please see this link for more information https://aka.ms/azsdk/python/pylint-guide".format( + "{} main package exited with linting error {}. Please see this link for more information https://aka.ms/azsdk/python/pylint-guide".format( package_name, e.returncode ) ) - if args.next and in_ci(): - from gh_tools.vnext_issue_creator import create_vnext_issue - - create_vnext_issue(package_dir, "pylint") - results.append(e.returncode) + + # Run pylint on tests and samples with appropriate pylintrc if they exist and next pylint is being used + if args.next: + tests_dir = os.path.join(package_dir, "tests") + samples_dir = os.path.join(package_dir, "samples") + + # Run tests with test_pylintrc + if os.path.exists(tests_dir): + try: + test_rcfile = os.path.join(REPO_ROOT, "eng/test_pylintrc") + logger.info( + [ + executable, + "-m", + "pylint", + "--rcfile={}".format(test_rcfile), + "--output-format=parseable", + tests_dir + ] + ) + results.append( + check_call( + [ + executable, + "-m", + "pylint", + "--rcfile={}".format(test_rcfile), + "--output-format=parseable", + tests_dir + ] + ) + ) + except CalledProcessError as e: + logger.error( + "{} tests exited with linting error {}. Please see this link for more information https://aka.ms/azsdk/python/pylint-guide".format( + package_name, e.returncode + ) + ) + results.append(e.returncode) + + # Run samples with main pylintrc + if os.path.exists(samples_dir): + try: + logger.info( + [ + executable, + "-m", + "pylint", + "--rcfile={}".format(rcFileLocation), + "--output-format=parseable", + samples_dir + ] + ) + results.append( + check_call( + [ + executable, + "-m", + "pylint", + "--rcfile={}".format(rcFileLocation), + "--output-format=parseable", + samples_dir + ] + ) + ) + except CalledProcessError as e: + logger.error( + "{} samples exited with linting error {}. Please see this link for more information https://aka.ms/azsdk/python/pylint-guide".format( + package_name, e.returncode + ) + ) + results.append(e.returncode) + + if args.next and in_ci() and any(result > 0 for result in results): + from gh_tools.vnext_issue_creator import create_vnext_issue + create_vnext_issue(package_dir, "pylint") if args.next and in_ci(): from gh_tools.vnext_issue_creator import close_vnext_issue diff --git a/eng/tools/azure-sdk-tools/azpysdk/pyright.py b/eng/tools/azure-sdk-tools/azpysdk/pyright.py index 779c90d9cae5..98059bac648b 100644 --- a/eng/tools/azure-sdk-tools/azpysdk/pyright.py +++ b/eng/tools/azure-sdk-tools/azpysdk/pyright.py @@ -15,7 +15,7 @@ from ci_tools.logging import logger -PYRIGHT_VERSION = "1.1.391" +PYRIGHT_VERSION = "1.1.405" REPO_ROOT = discover_repo_root() diff --git a/eng/tox/run_mypy.py b/eng/tox/run_mypy.py index 381d2c1e22a9..1e2a01291733 100644 --- a/eng/tox/run_mypy.py +++ b/eng/tox/run_mypy.py @@ -23,7 +23,7 @@ root_dir = os.path.abspath(os.path.join(os.path.abspath(__file__), "..", "..", "..")) -PYTHON_VERSION = "3.9" +PYTHON_VERSION = "3.10" if __name__ == "__main__": parser = argparse.ArgumentParser(description="Run mypy against target folder. ") diff --git a/eng/tox/run_pylint.py b/eng/tox/run_pylint.py index 2e6f1572e24c..29ab80dd1849 100644 --- a/eng/tox/run_pylint.py +++ b/eng/tox/run_pylint.py @@ -57,7 +57,12 @@ ) exit(0) + exit_code = 0 + + # Run pylint on main package try: + main_pylint_targets = [os.path.join(args.target_package, top_level_module)] + check_call( [ sys.executable, @@ -65,18 +70,73 @@ "pylint", "--rcfile={}".format(rcFileLocation), "--output-format=parseable", - os.path.join(args.target_package, top_level_module), - ] + ] + main_pylint_targets ) except CalledProcessError as e: logging.error( - "{} exited with linting error {}. Please see this link for more information https://aka.ms/azsdk/python/pylint-guide".format(pkg_details.name, e.returncode) + "{} main package exited with linting error {}. Please see this link for more information https://aka.ms/azsdk/python/pylint-guide".format(pkg_details.name, e.returncode) ) + exit_code = max(exit_code, e.returncode) + + # Run pylint on tests and samples with appropriate pylintrc if they exist and next pylint is being used + if args.next: + logging.info("Running with --next flag, checking for tests and samples directories") + tests_dir = os.path.join(args.target_package, "tests") + samples_dir = os.path.join(args.target_package, "samples") + + logging.info(f"Checking tests directory: {tests_dir}") + logging.info(f"Tests directory exists: {os.path.exists(tests_dir)}") + + # Run tests with test_pylintrc + if os.path.exists(tests_dir): + try: + test_rcfile = os.path.join(root_dir, "eng/test_pylintrc") + logging.info(f"Running pylint on tests with config: {test_rcfile}") + check_call( + [ + sys.executable, + "-m", + "pylint", + "--rcfile={}".format(test_rcfile), + "--output-format=parseable", + tests_dir + ] + ) + except CalledProcessError as e: + logging.error( + "{} tests exited with linting error {}. Please see this link for more information https://aka.ms/azsdk/python/pylint-guide".format(pkg_details.name, e.returncode) + ) + exit_code = max(exit_code, e.returncode) + + # Run samples with main pylintrc + logging.info(f"Checking samples directory: {samples_dir}") + logging.info(f"Samples directory exists: {os.path.exists(samples_dir)}") + if os.path.exists(samples_dir): + try: + logging.info(f"Running pylint on samples with config: {rcFileLocation}") + check_call( + [ + sys.executable, + "-m", + "pylint", + "--rcfile={}".format(rcFileLocation), + "--output-format=parseable", + samples_dir + ] + ) + except CalledProcessError as e: + logging.error( + "{} samples exited with linting error {}. Please see this link for more information https://aka.ms/azsdk/python/pylint-guide".format(pkg_details.name, e.returncode) + ) + exit_code = max(exit_code, e.returncode) + else: + logging.info("Not running with --next flag, skipping tests and samples") + + if exit_code > 0: if args.next and in_ci(): from gh_tools.vnext_issue_creator import create_vnext_issue create_vnext_issue(pkg_dir, "pylint") - - exit(1) + exit(exit_code) if args.next and in_ci(): from gh_tools.vnext_issue_creator import close_vnext_issue diff --git a/eng/tox/tox.ini b/eng/tox/tox.ini index 3e518878ab20..a5292f184e1d 100644 --- a/eng/tox/tox.ini +++ b/eng/tox/tox.ini @@ -110,7 +110,7 @@ deps = -rdev_requirements.txt commands = {[tox]pip_command} install pylint=={[testenv:pylint]pylint_version} - {[tox]pip_command} install azure-pylint-guidelines-checker==0.5.6 --index-url="https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-python/pypi/simple/" + {[tox]pip_command} install azure-pylint-guidelines-checker==0.5.7 --index-url="https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-python/pypi/simple/" python {repository_root}/eng/tox/create_package_and_install.py \ -d {envtmpdir}/dist \ -p {tox_root} \ @@ -120,7 +120,7 @@ commands = [testenv:next-pylint] description=Lints a package with pylint (version {[testenv:next-pylint]pylint_version}) -pylint_version=3.3.6 +pylint_version=4.0.4 skipsdist = true skip_install = true usedevelop = false @@ -165,7 +165,7 @@ commands = [testenv:mypy] description=Typechecks a package with mypy (version {[testenv:mypy]mypy_version}) -mypy_version=1.14.1 +mypy_version=1.18.1 skipsdist = true skip_install = true usedevelop = true @@ -190,7 +190,7 @@ commands = [testenv:next-mypy] description=Typechecks a package with the latest version of mypy -mypy_version=1.18.1 +mypy_version=1.19.1 skipsdist = true skip_install = true usedevelop = true @@ -217,7 +217,7 @@ commands = [testenv:pyright] description=Typechecks a package with pyright (version {[testenv:pyright]pyright_version}) -pyright_version=1.1.391 +pyright_version=1.1.405 skipsdist = true skip_install = true usedevelop = true @@ -239,7 +239,7 @@ commands = [testenv:next-pyright] description=Typechecks a package with the latest version of static type-checker pyright -pyright_version=1.1.405 +pyright_version=1.1.407 skipsdist = true skip_install = true usedevelop = true diff --git a/pylintrc b/pylintrc index 60540a9df272..6b3ea3408928 100644 --- a/pylintrc +++ b/pylintrc @@ -1,5 +1,5 @@ [MASTER] -py-version=3.9 +py-version=3.10 ignore-patterns=test_*,conftest,setup reports=no