diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 58676f3a..3c7365ef 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -30,24 +30,11 @@ repos: "--diff_command='diff'", "--warnings=-module-docstring,-function-docstring,-function-docstring-header,-print" ] - - - repo: local + - repo: https://github.com/astral-sh/ruff-pre-commit + # Ruff version. + rev: v0.1.9 hooks: - - id: black - name: black - entry: poetry run black - language: system - types: [ python ] - - - id: isort - name: isort - entry: poetry run isort - language: system - types: [ python ] - - - id: pyupgrade - name: pyupgrade - entry: poetry run pyupgrade - args: [ --py38-plus ] - language: system - types: [ python ] + - id: ruff-format + # Linting + - id: ruff + args: [ --fix ] diff --git a/poetry.lock b/poetry.lock index 8303142a..af6d608c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,48 +1,5 @@ # This file is automatically @generated by Poetry 1.4.2 and should not be changed by hand. -[[package]] -name = "black" -version = "23.11.0" -description = "The uncompromising code formatter." -category = "dev" -optional = false -python-versions = ">=3.8" -files = [ - {file = "black-23.11.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dbea0bb8575c6b6303cc65017b46351dc5953eea5c0a59d7b7e3a2d2f433a911"}, - {file = "black-23.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:412f56bab20ac85927f3a959230331de5614aecda1ede14b373083f62ec24e6f"}, - {file = "black-23.11.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d136ef5b418c81660ad847efe0e55c58c8208b77a57a28a503a5f345ccf01394"}, - {file = "black-23.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:6c1cac07e64433f646a9a838cdc00c9768b3c362805afc3fce341af0e6a9ae9f"}, - {file = "black-23.11.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cf57719e581cfd48c4efe28543fea3d139c6b6f1238b3f0102a9c73992cbb479"}, - {file = "black-23.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:698c1e0d5c43354ec5d6f4d914d0d553a9ada56c85415700b81dc90125aac244"}, - {file = "black-23.11.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:760415ccc20f9e8747084169110ef75d545f3b0932ee21368f63ac0fee86b221"}, - {file = "black-23.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:58e5f4d08a205b11800332920e285bd25e1a75c54953e05502052738fe16b3b5"}, - {file = "black-23.11.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:45aa1d4675964946e53ab81aeec7a37613c1cb71647b5394779e6efb79d6d187"}, - {file = "black-23.11.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4c44b7211a3a0570cc097e81135faa5f261264f4dfaa22bd5ee2875a4e773bd6"}, - {file = "black-23.11.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2a9acad1451632021ee0d146c8765782a0c3846e0e0ea46659d7c4f89d9b212b"}, - {file = "black-23.11.0-cp38-cp38-win_amd64.whl", hash = "sha256:fc7f6a44d52747e65a02558e1d807c82df1d66ffa80a601862040a43ec2e3142"}, - {file = "black-23.11.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7f622b6822f02bfaf2a5cd31fdb7cd86fcf33dab6ced5185c35f5db98260b055"}, - {file = "black-23.11.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:250d7e60f323fcfc8ea6c800d5eba12f7967400eb6c2d21ae85ad31c204fb1f4"}, - {file = "black-23.11.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5133f5507007ba08d8b7b263c7aa0f931af5ba88a29beacc4b2dc23fcefe9c06"}, - {file = "black-23.11.0-cp39-cp39-win_amd64.whl", hash = "sha256:421f3e44aa67138ab1b9bfbc22ee3780b22fa5b291e4db8ab7eee95200726b07"}, - {file = "black-23.11.0-py3-none-any.whl", hash = "sha256:54caaa703227c6e0c87b76326d0862184729a69b73d3b7305b6288e1d830067e"}, - {file = "black-23.11.0.tar.gz", hash = "sha256:4c68855825ff432d197229846f971bc4d6666ce90492e5b02013bcaca4d9ab05"}, -] - -[package.dependencies] -click = ">=8.0.0" -mypy-extensions = ">=0.4.3" -packaging = ">=22.0" -pathspec = ">=0.9.0" -platformdirs = ">=2" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} - -[package.extras] -colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.7.4)"] -jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] -uvloop = ["uvloop (>=0.15.2)"] - [[package]] name = "cfgv" version = "3.4.0" @@ -55,43 +12,16 @@ files = [ {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, ] -[[package]] -name = "click" -version = "8.1.7" -description = "Composable command line interface toolkit" -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, - {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - -[[package]] -name = "colorama" -version = "0.4.6" -description = "Cross-platform colored terminal text." -category = "dev" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -files = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] - [[package]] name = "distlib" -version = "0.3.7" +version = "0.3.8" description = "Distribution utilities" category = "dev" optional = false python-versions = "*" files = [ - {file = "distlib-0.3.7-py2.py3-none-any.whl", hash = "sha256:2e24928bc811348f0feb63014e97aaae3037f2cf48712d51ae61df7fd6075057"}, - {file = "distlib-0.3.7.tar.gz", hash = "sha256:9dafe54b34a028eafd95039d5e5d4851a13734540f1331060d31c9916e7147a8"}, + {file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"}, + {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, ] [[package]] @@ -113,49 +43,19 @@ typing = ["typing-extensions (>=4.8)"] [[package]] name = "identify" -version = "2.5.32" +version = "2.5.33" description = "File identification library for Python" category = "dev" optional = false python-versions = ">=3.8" files = [ - {file = "identify-2.5.32-py2.py3-none-any.whl", hash = "sha256:0b7656ef6cba81664b783352c73f8c24b39cf82f926f78f4550eda928e5e0545"}, - {file = "identify-2.5.32.tar.gz", hash = "sha256:5d9979348ec1a21c768ae07e0a652924538e8bce67313a73cb0f681cf08ba407"}, + {file = "identify-2.5.33-py2.py3-none-any.whl", hash = "sha256:d40ce5fcd762817627670da8a7d8d8e65f24342d14539c59488dc603bf662e34"}, + {file = "identify-2.5.33.tar.gz", hash = "sha256:161558f9fe4559e1557e1bff323e8631f6a0e4837f7497767c1782832f16b62d"}, ] [package.extras] license = ["ukkonen"] -[[package]] -name = "isort" -version = "5.12.0" -description = "A Python utility / library to sort Python imports." -category = "dev" -optional = false -python-versions = ">=3.8.0" -files = [ - {file = "isort-5.12.0-py3-none-any.whl", hash = "sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6"}, - {file = "isort-5.12.0.tar.gz", hash = "sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504"}, -] - -[package.extras] -colors = ["colorama (>=0.4.3)"] -pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", "requirementslib"] -plugins = ["setuptools"] -requirements-deprecated-finder = ["pip-api", "pipreqs"] - -[[package]] -name = "mypy-extensions" -version = "1.0.0" -description = "Type system extensions for programs checked with the mypy type checker." -category = "dev" -optional = false -python-versions = ">=3.5" -files = [ - {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, - {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, -] - [[package]] name = "nodeenv" version = "1.8.0" @@ -171,40 +71,16 @@ files = [ [package.dependencies] setuptools = "*" -[[package]] -name = "packaging" -version = "23.2" -description = "Core utilities for Python packages" -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, - {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, -] - -[[package]] -name = "pathspec" -version = "0.11.2" -description = "Utility library for gitignore style pattern matching of file paths." -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pathspec-0.11.2-py3-none-any.whl", hash = "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20"}, - {file = "pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"}, -] - [[package]] name = "platformdirs" -version = "4.0.0" +version = "4.1.0" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." category = "dev" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "platformdirs-4.0.0-py3-none-any.whl", hash = "sha256:118c954d7e949b35437270383a3f2531e99dd93cf7ce4dc8340d3356d30f173b"}, - {file = "platformdirs-4.0.0.tar.gz", hash = "sha256:cb633b2bcf10c51af60beb0ab06d2f1d69064b43abf4c185ca6b28865f3f9731"}, + {file = "platformdirs-4.1.0-py3-none-any.whl", hash = "sha256:11c8f37bcca40db96d8144522d925583bdb7a31f7b0e37e3ed4318400a8e2380"}, + {file = "platformdirs-4.1.0.tar.gz", hash = "sha256:906d548203468492d432bcb294d4bc2fff751bf84971fbb2c10918cc206ee420"}, ] [package.extras] @@ -230,21 +106,6 @@ nodeenv = ">=0.11.1" pyyaml = ">=5.1" virtualenv = ">=20.10.0" -[[package]] -name = "pyupgrade" -version = "3.15.0" -description = "A tool to automatically upgrade syntax for newer versions." -category = "dev" -optional = false -python-versions = ">=3.8.1" -files = [ - {file = "pyupgrade-3.15.0-py2.py3-none-any.whl", hash = "sha256:8dc8ebfaed43566e2c65994162795017c7db11f531558a74bc8aa077907bc305"}, - {file = "pyupgrade-3.15.0.tar.gz", hash = "sha256:a7fde381060d7c224f55aef7a30fae5ac93bbc428367d27e70a603bc2acd4f00"}, -] - -[package.dependencies] -tokenize-rt = ">=5.2.0" - [[package]] name = "pyyaml" version = "6.0.1" @@ -307,14 +168,14 @@ files = [ [[package]] name = "setuptools" -version = "69.0.2" +version = "69.0.3" description = "Easily download, build, install, upgrade, and uninstall Python packages" category = "dev" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-69.0.2-py3-none-any.whl", hash = "sha256:1e8fdff6797d3865f37397be788a4e3cba233608e9b509382a2777d25ebde7f2"}, - {file = "setuptools-69.0.2.tar.gz", hash = "sha256:735896e78a4742605974de002ac60562d286fa8051a7e2299445e8e8fbb01aa6"}, + {file = "setuptools-69.0.3-py3-none-any.whl", hash = "sha256:385eb4edd9c9d5c17540511303e39a147ce2fc04bc55289c322b9e5904fe2c05"}, + {file = "setuptools-69.0.3.tar.gz", hash = "sha256:be1af57fc409f93647f2e8e4573a142ed38724b8cdd389706a867bb4efcf1e78"}, ] [package.extras] @@ -322,42 +183,6 @@ docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] -[[package]] -name = "tokenize-rt" -version = "5.2.0" -description = "A wrapper around the stdlib `tokenize` which roundtrips." -category = "dev" -optional = false -python-versions = ">=3.8" -files = [ - {file = "tokenize_rt-5.2.0-py2.py3-none-any.whl", hash = "sha256:b79d41a65cfec71285433511b50271b05da3584a1da144a0752e9c621a285289"}, - {file = "tokenize_rt-5.2.0.tar.gz", hash = "sha256:9fe80f8a5c1edad2d3ede0f37481cc0cc1538a2f442c9c2f9e4feacd2792d054"}, -] - -[[package]] -name = "tomli" -version = "2.0.1" -description = "A lil' TOML parser" -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, - {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, -] - -[[package]] -name = "typing-extensions" -version = "4.8.0" -description = "Backported and Experimental Type Hints for Python 3.8+" -category = "dev" -optional = false -python-versions = ">=3.8" -files = [ - {file = "typing_extensions-4.8.0-py3-none-any.whl", hash = "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0"}, - {file = "typing_extensions-4.8.0.tar.gz", hash = "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"}, -] - [[package]] name = "virtualenv" version = "20.25.0" @@ -382,4 +207,4 @@ test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess [metadata] lock-version = "2.0" python-versions = "^3.8.1" -content-hash = "a91fb6336bbad5c5185538570c381ef6bf841971d34d805513399732167e1645" +content-hash = "971b16e30d3a4cea9f42507c0c2549f7a6383c48b77a175a9729fa4df5b8fb47" diff --git a/pyproject.toml b/pyproject.toml index 84e2d5b9..6010d6ee 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,18 +7,8 @@ authors = ["Martin Medler"] [tool.poetry.dependencies] python = "^3.8.1" -[tool.black] -line-length = 120 - -[tool.isort] -multi_line_output = 3 -profile = "black" - [tool.poetry.dev-dependencies] -black = "^23.11.0" -isort = "^5.12.0" pre-commit = "^3.5.0" -pyupgrade = "^3.15.0" [build-system] requires = ["poetry-core>=1.0.0"] diff --git a/ruff.toml b/ruff.toml new file mode 100644 index 00000000..fe787184 --- /dev/null +++ b/ruff.toml @@ -0,0 +1,47 @@ +exclude = [ + ".git", + ".ruff_cache", +] +line-length = 120 +indent-width = 4 +# Minimum Python version which we support +target-version = "py38" + +[format] +quote-style = "double" +indent-style = "space" +# We want to be able to force spreading things over multiple lines by adding a ',' for readability +skip-magic-trailing-comma = false +line-ending = "auto" +# We have no Python code in documentation +docstring-code-format = false + +[lint] +select = [ + # pycodestyle + "E", + # Pyflakes + "F", + # pyupgrade + "UP", + # flake8-builtins + "A", + # flake8-bugbear + "B", + # flake8-simplify + "SIM", + # isort + "I", + # Pylint + "PL", + # Ruff-specific + "RUF" +] +ignore = [ + # It is fine tha some line are longer than the length limit + "E501", + # High false positive rate. Often a plain number is the most expressive, e.g. when checling lengths. + "PLR2004", + # Using Optinal and '= None' at once is overly verbose and hurts readability + "RUF013", +] diff --git a/src/analyze_includes/evaluate_includes.py b/src/analyze_includes/evaluate_includes.py index e80fc4d4..78ba5566 100644 --- a/src/analyze_includes/evaluate_includes.py +++ b/src/analyze_includes/evaluate_includes.py @@ -15,10 +15,7 @@ def does_include_match_available_files( ) -> bool: for header in header_files: for inc in include_paths: - if inc: - possible_file = inc + "/" + include_statement - else: - possible_file = include_statement + possible_file = inc + "/" + include_statement if inc else include_statement if possible_file == header: return True return False @@ -57,7 +54,7 @@ def _check_for_invalid_includes( ) if not legal_include: # Might be a relative include - roots_for_relative_includes = [Path(root) for root in [inc.file.parent] + include_paths] + roots_for_relative_includes = [Path(root) for root in [str(inc.file.parent), *include_paths]] for root in roots_for_relative_includes: path_matching_include_statement = (root / inc.include).resolve() diff --git a/src/analyze_includes/parse_source.py b/src/analyze_includes/parse_source.py index 6c6724cb..5eb0741d 100644 --- a/src/analyze_includes/parse_source.py +++ b/src/analyze_includes/parse_source.py @@ -99,14 +99,13 @@ def get_includes_from_file(file: Path, defines: List[str], include_paths: List[s for include in re.findall(r"^\s*#include\s*(.+)", output_sink.getvalue(), re.MULTILINE): if include.startswith(('"', "<")) and include.endswith(('"', ">")): included_paths.append(include) - else: + elif include in pre_processor.macros: # Either a malformed include statement or an include path defined through a pre processor token. # We ignore malformed include paths as they violate our assumptions of use. - if include in pre_processor.macros: - # 'macros' is a {str: 'Macro'} dictionary based on pcpp.parser.Macro. - # The value is a list of 'LexToken' classes from 'ply.lex.LexToken'. - # In all our tests with include statements the list had always just one element. - included_paths.append(pre_processor.macros[include].value[0].value) + # 'macros' is a {str: 'Macro'} dictionary based on pcpp.parser.Macro. + # The value is a list of 'LexToken' classes from 'ply.lex.LexToken'. + # In all our tests with include statements the list had always just one element. + included_paths.append(pre_processor.macros[include].value[0].value) return [Include(file=file, include=include.lstrip('"<').rstrip('">')) for include in included_paths] diff --git a/src/analyze_includes/test/system_under_inspection_test.py b/src/analyze_includes/test/system_under_inspection_test.py index beba5009..53e4e024 100644 --- a/src/analyze_includes/test/system_under_inspection_test.py +++ b/src/analyze_includes/test/system_under_inspection_test.py @@ -24,7 +24,7 @@ def test_default_ctor(self): def test_no_usage_resetting(self): unit = UsageStatusTracker() - with self.assertRaises(Exception): + with self.assertRaisesRegex(Exception, "Resetting the usage is not supported"): unit.update(UsageStatus.NONE) def test_update_to_public_and_then_private(self): diff --git a/src/apply_fixes/buildozer_executor.py b/src/apply_fixes/buildozer_executor.py index e71839c5..6029ebbe 100644 --- a/src/apply_fixes/buildozer_executor.py +++ b/src/apply_fixes/buildozer_executor.py @@ -27,7 +27,7 @@ def summary(self) -> Summary: return self._summary def execute(self, task: str, target: str) -> None: - command = self._base_cmd + [task, target] + command = [*self._base_cmd, task, target] logging.log(logging.INFO if self._dry else logging.DEBUG, f"Executing buildozer command: {command}") process = subprocess.run(command, cwd=self._workspace, check=False, capture_output=True) self._summary.add_command(cmd=command, buildozer_result=process.returncode) diff --git a/src/apply_fixes/test/summary_test.py b/src/apply_fixes/test/summary_test.py index 8d58af1c..0080a48e 100644 --- a/src/apply_fixes/test/summary_test.py +++ b/src/apply_fixes/test/summary_test.py @@ -21,7 +21,7 @@ def test_add_failing_command(self): def test_raise_on_unexpected_return_code(self): unit = Summary() - with self.assertRaises(Exception): + with self.assertRaisesRegex(Exception, "failed with the unexpected return code"): unit.add_command(cmd=["foo", "bar"], buildozer_result=5) diff --git a/test/apply_fixes/test_case.py b/test/apply_fixes/test_case.py index ca3070fb..26528926 100644 --- a/test/apply_fixes/test_case.py +++ b/test/apply_fixes/test_case.py @@ -93,14 +93,16 @@ def _get_target_attribute(self, target: str, attribute: str) -> Set["str"]: def _run_cmd(self, cmd: List[str], **kwargs) -> None: logging.debug(f"Executing command: {cmd}") + check = kwargs.pop("check", True) if logging.getLogger().isEnabledFor(logging.DEBUG): - subprocess.run(cmd, cwd=self._workspace, **kwargs) + subprocess.run(cmd, cwd=self._workspace, check=check, **kwargs) else: - subprocess.run(cmd, cwd=self._workspace, capture_output=True, **kwargs) + subprocess.run(cmd, cwd=self._workspace, capture_output=True, check=check, **kwargs) def _run_and_capture_cmd(self, cmd: List[str], **kwargs) -> subprocess.CompletedProcess: logging.debug(f"Executing command: {cmd}") - process = subprocess.run(cmd, cwd=self._workspace, capture_output=True, text=True, **kwargs) + check = kwargs.pop("check", True) + process = subprocess.run(cmd, cwd=self._workspace, capture_output=True, text=True, check=check, **kwargs) logging.debug(process.stdout) logging.debug(process.stderr) return process diff --git a/test/apply_fixes/tool_cli/test_fail_if_no_fix_option_is_provided.py b/test/apply_fixes/tool_cli/test_fail_if_no_fix_option_is_provided.py index 8bdbcd9c..84e47e86 100644 --- a/test/apply_fixes/tool_cli/test_fail_if_no_fix_option_is_provided.py +++ b/test/apply_fixes/tool_cli/test_fail_if_no_fix_option_is_provided.py @@ -15,7 +15,7 @@ def execute_test_logic(self) -> Result: check=False, ) if process.returncode == 0: - return Error(f"Expected an exception, but none occurred") + return Error("Expected an exception, but none occurred") if (expected_error := "Please choose at least one of the 'fix-..' options") not in process.stderr: return self._make_unexpected_output_error(expected=expected_error, output=process.stderr) diff --git a/test/apply_fixes/tool_cli/test_fail_on_missing_report_files.py b/test/apply_fixes/tool_cli/test_fail_on_missing_report_files.py index 0fad1a77..f00c7770 100644 --- a/test/apply_fixes/tool_cli/test_fail_on_missing_report_files.py +++ b/test/apply_fixes/tool_cli/test_fail_on_missing_report_files.py @@ -23,7 +23,7 @@ def execute_test_logic(self) -> Result: ) if process.returncode == 0: - return Error(f"Expected an exception, but none occurred") + return Error("Expected an exception, but none occurred") if (expected_error := "ERROR: Did not find any DWYU report files.") not in process.stderr: return self._make_unexpected_output_error(expected=expected_error, output=process.stderr) diff --git a/test/aspect/execute_tests.py b/test/aspect/execute_tests.py index dfe27be9..0f31962b 100755 --- a/test/aspect/execute_tests.py +++ b/test/aspect/execute_tests.py @@ -25,37 +25,38 @@ # When Bazel 7.0.0 releases we have to look again at the flags and check if more flags are available VERSION_SPECIFIC_ARGS = { # We support Bazel's modern dependency management system, but it works only as desired with a recent Bazel version - "--experimental_enable_bzlmod=false": CompatibleVersions(max="6.1.99"), + "--experimental_enable_bzlmod=false": CompatibleVersions(maximum="6.1.99"), # We are not yet sure if we really want to lock the bzlmod resolution down given we test with various Bazel versions # and configurations. It seems the main benefits of the lock file are not having to reanalyze the central registry # when working without a cached workspace and being safeguarded against changed or yanked modules in the central # registry. Both don't matter much to us right now. - "--lockfile_mode=off": CompatibleVersions(min="6.2.0"), + "--lockfile_mode=off": CompatibleVersions(minimum="6.2.0"), # Incompatible changes - "--incompatible_legacy_local_fallback=false": CompatibleVersions(min="5.0.0"), # false is the forward path behavior - "--incompatible_enforce_config_setting_visibility": CompatibleVersions(min="5.0.0"), - "--incompatible_config_setting_private_default_visibility": CompatibleVersions(min="5.0.0"), - "--incompatible_disable_target_provider_fields": CompatibleVersions(min="5.0.0"), - "--incompatible_struct_has_no_methods": CompatibleVersions(min="5.0.0"), - "--incompatible_use_platforms_repo_for_constraints": CompatibleVersions(min="5.0.0", max="6.99.99"), - "--incompatible_disallow_empty_glob": CompatibleVersions(min="5.0.0"), - "--incompatible_no_implicit_file_export": CompatibleVersions(min="5.0.0"), - "--incompatible_use_cc_configure_from_rules_cc": CompatibleVersions(min="5.0.0"), - "--incompatible_default_to_explicit_init_py": CompatibleVersions(min="5.0.0"), - "--incompatible_exclusive_test_sandboxed": CompatibleVersions(min="5.0.0"), - "--incompatible_strict_action_env": CompatibleVersions(min="5.0.0"), - "--incompatible_disable_starlark_host_transitions": CompatibleVersions(min="6.0.0"), - "--incompatible_sandbox_hermetic_tmp": CompatibleVersions(min="6.0.0"), - "--incompatible_check_testonly_for_output_files": CompatibleVersions(min="6.0.0"), - "--incompatible_check_visibility_for_toolchains": CompatibleVersions(min="7.0.0"), - "--incompatible_auto_exec_groups": CompatibleVersions(min="7.0.0"), - "--incompatible_disable_non_executable_java_binary": CompatibleVersions(min="7.0.0"), + "--incompatible_legacy_local_fallback=false": CompatibleVersions(minimum="5.0.0"), + # false is the forward path behavior + "--incompatible_enforce_config_setting_visibility": CompatibleVersions(minimum="5.0.0"), + "--incompatible_config_setting_private_default_visibility": CompatibleVersions(minimum="5.0.0"), + "--incompatible_disable_target_provider_fields": CompatibleVersions(minimum="5.0.0"), + "--incompatible_struct_has_no_methods": CompatibleVersions(minimum="5.0.0"), + "--incompatible_use_platforms_repo_for_constraints": CompatibleVersions(minimum="5.0.0", maximum="6.99.99"), + "--incompatible_disallow_empty_glob": CompatibleVersions(minimum="5.0.0"), + "--incompatible_no_implicit_file_export": CompatibleVersions(minimum="5.0.0"), + "--incompatible_use_cc_configure_from_rules_cc": CompatibleVersions(minimum="5.0.0"), + "--incompatible_default_to_explicit_init_py": CompatibleVersions(minimum="5.0.0"), + "--incompatible_exclusive_test_sandboxed": CompatibleVersions(minimum="5.0.0"), + "--incompatible_strict_action_env": CompatibleVersions(minimum="5.0.0"), + "--incompatible_disable_starlark_host_transitions": CompatibleVersions(minimum="6.0.0"), + "--incompatible_sandbox_hermetic_tmp": CompatibleVersions(minimum="6.0.0"), + "--incompatible_check_testonly_for_output_files": CompatibleVersions(minimum="6.0.0"), + "--incompatible_check_visibility_for_toolchains": CompatibleVersions(minimum="7.0.0"), + "--incompatible_auto_exec_groups": CompatibleVersions(minimum="7.0.0"), + "--incompatible_disable_non_executable_java_binary": CompatibleVersions(minimum="7.0.0"), # Theoretically of interest for us, but rules_python does not comply to this. - # "--incompatible_stop_exporting_language_modules": CompatibleVersions(min="6.0.0"), + # "--incompatible_stop_exporting_language_modules": CompatibleVersions(minimum="6.0.0"), # Theoretically interesting for our project, but Bazel itself does not adhere to it - # "--incompatible_python_disallow_native_rules": CompatibleVersions(min="7.0.0"), + # "--incompatible_python_disallow_native_rules": CompatibleVersions(minimum="7.0.0"), # Theoretically of interest for us, but rules_python does not comply to this. - # "--incompatible_disallow_struct_provider_syntax": CompatibleVersions(min="7.0.0"), + # "--incompatible_disallow_struct_provider_syntax": CompatibleVersions(minimum="7.0.0"), } DEFAULT_ASPECT = "//test/aspect:aspect.bzl%dwyu_default_aspect" diff --git a/test/aspect/execute_tests_impl.py b/test/aspect/execute_tests_impl.py index 3abb7cf5..396a76a8 100644 --- a/test/aspect/execute_tests_impl.py +++ b/test/aspect/execute_tests_impl.py @@ -122,20 +122,17 @@ def _find_line_with(lines: List[str], val: str) -> Optional[int]: def _has_errors(error_lines: List[str], expected_errors: List[str]) -> bool: if len(error_lines) != len(expected_errors): return False - for error in expected_errors: - if not any(error in line for line in error_lines): - return False - return True + return all(any(error in line for line in error_lines) for error in expected_errors) @dataclass class CompatibleVersions: - min: str = "" - max: str = "" + minimum: str = "" + maximum: str = "" def is_compatible_to(self, version: str) -> bool: - comply_with_min_version = version >= self.min if self.min else True - comply_with_max_version = version <= self.max if self.max else True + comply_with_min_version = version >= self.minimum if self.minimum else True + comply_with_max_version = version <= self.maximum if self.maximum else True return comply_with_min_version and comply_with_max_version @@ -144,7 +141,7 @@ class TestCase: name: str cmd: TestCmd expected: ExpectedResult - compatible_versions: CompatibleVersions = CompatibleVersions() + compatible_versions: CompatibleVersions = field(default_factory=CompatibleVersions) @dataclass @@ -174,7 +171,7 @@ def bazel_binary() -> str: def make_cmd(test_cmd: TestCmd, startup_args: List[str], extra_args: List[str]) -> List[str]: - cmd = [bazel_binary()] + startup_args + ["build", "--noshow_progress"] + cmd = [bazel_binary(), *startup_args, "build", "--noshow_progress"] if test_cmd.aspect: cmd.extend([f"--aspects={test_cmd.aspect}", "--output_groups=dwyu"]) cmd.extend(extra_args) @@ -267,15 +264,8 @@ def main( tested_versions: List[TestedVersions], version_specific_args: Dict[str, CompatibleVersions], ) -> int: - if args.bazel and args.python: - versions = [TestedVersions(bazel=args.bazel, python=args.python)] - else: - versions = tested_versions - - if args.test: - active_tests = [tc for tc in test_cases if tc.name in args.test] - else: - active_tests = test_cases + versions = [TestedVersions(bazel=args.bazel, python=args.python)] if args.bazel and args.python else tested_versions + active_tests = [tc for tc in test_cases if tc.name in args.test] if args.test else test_cases failed_tests = execute_tests( versions=versions, tests=active_tests, version_specific_args=version_specific_args, verbose=args.verbose diff --git a/test/aspect/execute_tests_utest.py b/test/aspect/execute_tests_utest.py index 1f3401af..8946d635 100644 --- a/test/aspect/execute_tests_utest.py +++ b/test/aspect/execute_tests_utest.py @@ -170,12 +170,12 @@ def _base_cmd(startup_args=None): bazel = which("bazelisk") or which("bazel") build_with_default_options = ["build", "--noshow_progress"] if startup_args: - return [bazel] + startup_args + build_with_default_options - return [bazel] + build_with_default_options + return [bazel, *startup_args, *build_with_default_options] + return [bazel, *build_with_default_options] def test_basic_cmd(self): cmd = make_cmd(test_cmd=TestCmd(target="//foo:bar"), extra_args=[], startup_args=[]) - self.assertEqual(cmd, self._base_cmd() + ["--", "//foo:bar"]) + self.assertEqual(cmd, [*self._base_cmd(), "--", "//foo:bar"]) def test_complex_test_cmd(self): cmd = make_cmd( @@ -189,8 +189,15 @@ def test_complex_test_cmd(self): ) self.assertEqual( cmd, - self._base_cmd() - + ["--aspects=//some/aspect.bzl", "--output_groups=dwyu", "--abc", "--cba", "--", "//foo:bar"], + [ + *self._base_cmd(), + "--aspects=//some/aspect.bzl", + "--output_groups=dwyu", + "--abc", + "--cba", + "--", + "//foo:bar", + ], ) def test_extra_args_on_top_of_test_cmd(self): @@ -205,8 +212,8 @@ def test_extra_args_on_top_of_test_cmd(self): ) self.assertEqual( cmd, - self._base_cmd(startup_args=["--some_startup_arg"]) - + [ + [ + *self._base_cmd(startup_args=["--some_startup_arg"]), "--aspects=//some/aspect.bzl", "--output_groups=dwyu", "--outside_arg", @@ -222,19 +229,19 @@ def test_no_limits(self): self.assertTrue(CompatibleVersions().is_compatible_to("1.0.0")) def test_above_min_version(self): - self.assertTrue(CompatibleVersions(min="0.9.9").is_compatible_to("1.0.0")) + self.assertTrue(CompatibleVersions(minimum="0.9.9").is_compatible_to("1.0.0")) def test_below_min_version(self): - self.assertFalse(CompatibleVersions(min="1.1.9").is_compatible_to("1.0.0")) + self.assertFalse(CompatibleVersions(minimum="1.1.9").is_compatible_to("1.0.0")) def test_below_max_version(self): - self.assertTrue(CompatibleVersions(max="1.1.0").is_compatible_to("1.0.0")) + self.assertTrue(CompatibleVersions(maximum="1.1.0").is_compatible_to("1.0.0")) def test_above_max_version(self): - self.assertFalse(CompatibleVersions(max="0.9.0").is_compatible_to("1.0.0")) + self.assertFalse(CompatibleVersions(maximum="0.9.0").is_compatible_to("1.0.0")) def test_inside_interval(self): - self.assertTrue(CompatibleVersions(min="0.9.0", max="1.1.0").is_compatible_to("1.0.0")) + self.assertTrue(CompatibleVersions(minimum="0.9.0", maximum="1.1.0").is_compatible_to("1.0.0")) if __name__ == "__main__":