diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c72d23b..8003605 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -63,13 +63,13 @@ jobs: run: python -m pytest -v --cov=cmake_pc_hooks --cov-report=xml --cov-report=term-missing --cov-branch - name: Upload coverage reports to Codecov - uses: codecov/codecov-action@v4 + uses: codecov/codecov-action@v5 with: token: ${{ secrets.CODECOV_TOKEN }} - name: Save coverage report if: matrix.python == '3.11' && runner.os == 'Linux' - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: coverage path: coverage.xml @@ -192,12 +192,12 @@ jobs: fetch-depth: 0 - name: Fetch coverage report - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: coverage - - name: SonarCloud Scan - uses: SonarSource/sonarcloud-github-action@master + - name: SonarQube Scan + uses: SonarSource/sonarqube-scan-action@v5 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} @@ -208,7 +208,7 @@ jobs: needs: unit-tests steps: - name: Fetch coverage report - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: coverage diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 853491a..7e25a99 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,7 +6,7 @@ repos: - id: check-useless-excludes - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.6.0 + rev: v6.0.0 hooks: - id: check-added-large-files - id: check-case-conflict @@ -25,46 +25,45 @@ repos: - id: remove-tabs - repo: https://github.com/PyCQA/doc8/ - rev: v1.1.1 + rev: v2.0.0 hooks: - id: doc8 require_serial: false additional_dependencies: [tomli] - repo: https://github.com/codespell-project/codespell - rev: v2.3.0 + rev: v2.4.1 hooks: - id: codespell files: .*\.(py|txt|cmake|md|rst|sh|ps1|hpp|tpp|cpp|cc)$ args: [-I, .codespell.allow] - repo: https://github.com/shellcheck-py/shellcheck-py - rev: v0.10.0.1 + rev: v0.11.0.1 hooks: - id: shellcheck require_serial: false args: [-x, --severity=info] - repo: https://github.com/adrienverge/yamllint.git - rev: v1.35.1 + rev: v1.37.1 hooks: - id: yamllint - repo: https://github.com/asottile/blacken-docs - rev: 1.16.0 + rev: 1.19.1 hooks: - id: blacken-docs args: [-S, -l, '120'] additional_dependencies: [black==22.12.0] - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.4.7 + rev: v0.12.9 hooks: - id: ruff-format name: ruff (format) - args: [--preview] - - id: ruff + - id: ruff-check name: ruff (fix) alias: ruff-fix args: diff --git a/CHANGELOG.md b/CHANGELOG.md index cb80b58..26d082f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Repository - Clarify where to put the settings in `pyproject.toml` +- Update GitHub Action `actions/download-artifact` to v4 +- Update GitHub Action `actions/upload-artifact` to v4 +- Update GitHub Action `codecov/codecov-action` from v4 to v5 +- Use GitHub Action `SonarSource/sonarqube-scan-action` v5 +- Update `github.com/pre-commit/pre-commit-hooks` hook to v6.0.0 +- Update `https://github.com/PyCQA/doc8/` hook to v2.0.0 +- Update `github.com/codespell-project/codespell` hook to v2.4.1 +- Update `github.com/shellcheck-py/shellcheck-py` hook to v0.11.0.1 +- Update `github.com/adrienverge/yamllint.git` hook to v1.37.1 +- Update `github.com/asottile/blacken-docs` hook to 1.19.1 +- Update `github.com/astral-sh/ruff-pre-commit` hook to v0.12.9 ## [v1.9.6] - 2024-06-02 diff --git a/cmake_pc_hooks/_argparse.py b/cmake_pc_hooks/_argparse.py index 652ade1..6a2f5ec 100644 --- a/cmake_pc_hooks/_argparse.py +++ b/cmake_pc_hooks/_argparse.py @@ -25,6 +25,8 @@ import toml +log = logging.getLogger(__name__) + # ============================================================================== @@ -110,7 +112,11 @@ def executable_path(path: str) -> Path: def _load_data_from_toml( - path: Path, section: str, *, path_must_exist: bool = True, section_must_exist: bool = True + path: Path, + section: str, + *, + path_must_exist: bool = True, + section_must_exist: bool = True, ) -> dict: """ Load a TOML file and return the corresponding config dictionary. @@ -127,23 +133,23 @@ def _load_data_from_toml( section_must_exist: Whether a missing section in the TOML file is considered an error or not """ try: - with path.open(mode='r') as fd: + with path.open(mode='r', encoding='utf-8') as fd: config = toml.load(fd) if section: for sub_section in section.split('.'): config = config[sub_section] - logging.debug('Loading data from %s table of %s', section, path) + log.debug('Loading data from %s table of %s', section, path) else: config = {key: value for key, value in config.items() if not isinstance(value, dict)} - logging.debug('Loading data from root table of %s', path) + log.debug('Loading data from root table of %s', path) except FileNotFoundError as err: if path_must_exist: raise TOMLFileNotFoundError(path) from err - logging.debug('TOML file %s does not exist (not an error)', str(path)) + log.debug('TOML file %s does not exist (not an error)', str(path)) except KeyError as err: if section_must_exist: raise TOMLSectionKeyError(section, path) from err - logging.debug('TOML file %s does not have a %s section (not an error)', str(path), section) + log.debug('TOML file %s does not have a %s section (not an error)', str(path), section) else: return config return {} @@ -245,7 +251,9 @@ def parse_known_args( if self._default_config_name is not None: namespace = self._load_from_toml( - namespace=namespace, path=Path(self._default_config_name), path_must_exist=False + namespace=namespace, + path=Path(self._default_config_name), + path_must_exist=False, ) namespace, args = super().parse_known_args(args=args, namespace=namespace) @@ -265,11 +273,13 @@ def parse_known_args( if namespace.dump_toml: exclude_keys = {'positionals', 'dump_toml'} print( - toml.dumps({ - key: value - for key, value in vars(namespace).items() - if value != self._default_args[key] and key not in exclude_keys - }) + toml.dumps( + { + key: value + for key, value in vars(namespace).items() + if value != self._default_args[key] and key not in exclude_keys + } + ) ) sys.exit(0) @@ -297,7 +307,10 @@ def _load_from_toml( # noqa: PLR0913 overridable_keys: List of keys that can be overridden by values in the TOML file """ config = _load_data_from_toml( - path, section, path_must_exist=path_must_exist, section_must_exist=section_must_exist + path, + section, + path_must_exist=path_must_exist, + section_must_exist=section_must_exist, ) for key, value in config.items(): @@ -309,9 +322,9 @@ def _load_from_toml( # noqa: PLR0913 if default_value is not None and not isinstance(value, type(default_value)): raise TOMLTypeError(type(value), type(default_value), key) if overridable_keys is not None and key not in overridable_keys: - logging.debug(' skipping non-overridable key: "%s"', key) + log.debug(' skipping non-overridable key: "%s"', key) continue - logging.debug(' setting %s = %s', key, value) + log.debug(' setting %s = %s', key, value) setattr(namespace, key, value) return namespace diff --git a/cmake_pc_hooks/_call_process.py b/cmake_pc_hooks/_call_process.py index 2cbb336..f62eb42 100644 --- a/cmake_pc_hooks/_call_process.py +++ b/cmake_pc_hooks/_call_process.py @@ -22,6 +22,8 @@ import attrs +log = logging.getLogger(__name__) + @attrs.define(slots=True) class History: # pylint: disable=too-few-public-methods @@ -64,9 +66,9 @@ def call_process(args: list, **kwargs: any) -> History: else: ret = History(sp_child.stdout.decode(), sp_child.stderr.decode(), sp_child.returncode) - logging.debug('command `%s` exited with %d', ' '.join(args), ret.returncode) + log.debug('command `%s` exited with %d', ' '.join(args), ret.returncode) for line in ret.stdout.split('\n'): - logging.debug('(stdout) %s', line) + log.debug('(stdout) %s', line) for line in ret.stderr.split('\n'): - logging.debug('(stderr) %s', line) + log.debug('(stderr) %s', line) return ret diff --git a/cmake_pc_hooks/_cmake.py b/cmake_pc_hooks/_cmake.py index 0d22abf..15946d0 100644 --- a/cmake_pc_hooks/_cmake.py +++ b/cmake_pc_hooks/_cmake.py @@ -32,6 +32,8 @@ from . import _argparse, _call_process +log = logging.getLogger(__name__) + # ============================================================================== @@ -267,7 +269,7 @@ def resolve_build_directory(self, build_dir_list=None, *, automatic_discovery=Tr build_dir_list = [] if build_dir_list is None else [Path(path) for path in build_dir_list] for build_dir in build_dir_list: if build_dir.exists() and Path(build_dir, 'CMakeCache.txt').exists(): - logging.debug( + log.debug( 'Located valid build directory with CMakeCache.txt at: %s', str(build_dir), ) @@ -278,12 +280,12 @@ def resolve_build_directory(self, build_dir_list=None, *, automatic_discovery=Tr if automatic_discovery: for path in sorted(self.source_dir.glob('*')): if path.is_dir() and (path / 'CMakeCache.txt').exists(): - logging.info('Automatic build dir discovery resulted in: %s', str(path)) + log.info('Automatic build dir discovery resulted in: %s', str(path)) self.build_dir = path return if self.no_cmake_configure: - logging.info('Unable to locate a valid build directory. Will not be creating one') + log.info('Unable to locate a valid build directory. Will not be creating one') self.build_dir = None return @@ -291,7 +293,7 @@ def resolve_build_directory(self, build_dir_list=None, *, automatic_discovery=Tr self.build_dir = self.source_dir / self.DEFAULT_BUILD_DIR else: self.build_dir = Path(build_dir_list[0]).resolve() - logging.info( + log.info( 'Unable to locate a valid build directory. Will be creating one at %s', str(self.build_dir), ) @@ -379,11 +381,11 @@ def configure(self, command, *, clean_build=False): clean_build (bool): Clean build directory before calling CMake configure """ if self.no_cmake_configure: - logging.debug('Not calling CMake configure') + log.debug('Not calling CMake configure') return if self.source_dir is None: - logging.error('No source dir was for CMake! Did you call `setup_cmake_args()`?') + log.error('No source dir was for CMake! Did you call `setup_cmake_args()`?') sys.exit(1) cmake_configure_lock_file = Path(self.build_dir, '_cmake_configure_lock') @@ -396,7 +398,7 @@ def configure(self, command, *, clean_build=False): try: with cmake_configure_try_lock.acquire(blocking=False): # noqa: SIM117 with cmake_configure_lock.write_lock(): - logging.debug( + log.debug( 'Command %s with id %s is running CMake configure', command, os.getpid(), @@ -408,23 +410,23 @@ def configure(self, command, *, clean_build=False): ), clean_build=clean_build, ) - logging.debug( + log.debug( 'Command %s with id %s is done running CMake configure', command, os.getpid(), ) except filelock.Timeout: - logging.debug( + log.debug( 'Command %s with id %s is not running CMake configure and waiting', command, os.getpid(), ) with cmake_configure_lock.read_lock(): - logging.debug('Command %s with id %s is done waiting', command, os.getpid()) + log.debug('Command %s with id %s is done waiting', command, os.getpid()) returncode = 0 if returncode != 0: - logging.error('CMake configure step failed. See output for more information.') + log.error('CMake configure step failed. See output for more information.') sys.exit(returncode) if self.cmake_trace_log is not None: @@ -439,12 +441,14 @@ def _call_cmake(self, extra_args=None): [*command, str(self.source_dir), *self.cmake_args, *extra_args], cwd=str(self.build_dir), ) - result.stdout = '\n'.join([ - f'Running CMake with: {[*command, str(self.source_dir), *self.cmake_args]}', - f' from within {self.build_dir}', - result.stdout, - '', - ]) + result.stdout = '\n'.join( + [ + f'Running CMake with: {[*command, str(self.source_dir), *self.cmake_args]}', + f' from within {self.build_dir}', + result.stdout, + '', + ] + ) return result @@ -461,11 +465,13 @@ def _configure(self, lock_files, clean_build): extra_args = [] if self.cmake_trace_log: - extra_args.extend([ - '--trace-expand', - '--trace-format=json-v1', - f'--trace-redirect={self.cmake_trace_log}', - ]) + extra_args.extend( + [ + '--trace-expand', + '--trace-format=json-v1', + f'--trace-redirect={self.cmake_trace_log}', + ] + ) result = self._call_cmake(extra_args=extra_args) @@ -480,16 +486,16 @@ def _configure(self, lock_files, clean_build): return result.returncode def _parse_cmake_trace_log(self): - logging.info('attempting to parse CMake trace log to detect calls to configure_file()') + log.info('attempting to parse CMake trace log to detect calls to configure_file()') self.cmake_configured_files = [] if not self.cmake_trace_log: - logging.info('no trace log provided, aborting.') + log.info('no trace log provided, aborting.') return result = self._call_cmake(extra_args=['-N', '-LA']) if result.returncode != 0: - logging.error('failed to retrieve CMake cache variables') + log.error('failed to retrieve CMake cache variables') return cmake_cache_variables = {} @@ -524,7 +530,7 @@ def _is_relevant_configure_file_call(json_data): input_file, configured_file = (Path(arg) for arg in configure_file_call['args'][:2]) if not configured_file.is_absolute(): configured_file = self.build_dir / configured_file - logging.debug( + log.debug( 'detected call to configure_file(%s %s [...])', str(input_file), str(configured_file), diff --git a/cmake_pc_hooks/_utils.py b/cmake_pc_hooks/_utils.py index e887e2e..1dbd289 100644 --- a/cmake_pc_hooks/_utils.py +++ b/cmake_pc_hooks/_utils.py @@ -31,6 +31,8 @@ logging.basicConfig(level=_LOGLEVEL, format='%(levelname)-5s:cmake-pc-hooks:%(message)s') logging.getLogger('filelock').setLevel(logging.WARNING) +log = logging.getLogger(__name__) + class CMakePresetError(Exception): """Exception raised if a command line incompatibility with --preset is detected.""" @@ -88,8 +90,7 @@ def parse_args(self, args): '--read-json-db', action='store_true', help=( - 'Run hooks on files found in compile_commands.json ' - '(if found and in addition to files specified on CLI)' + 'Run hooks on files found in compile_commands.json (if found and in addition to files specified on CLI)' ), ) @@ -137,7 +138,7 @@ def run(self): for filename in self.files: self.run_command([filename]) else: - logging.error('No files to process!') + log.error('No files to process!') sys.exit(1) has_errors = False @@ -172,9 +173,9 @@ def _resolve_compilation_database(cmake_build_dir: Path, build_dir_list: list[Pa for build_dir in build_dir_list: path = Path(build_dir, 'compile_commands.json') if build_dir.exists() and path.exists(): - logging.debug('Located valid compilation database at: %s', str(path)) + log.debug('Located valid compilation database at: %s', str(path)) return path - logging.debug('No valid compilation database located') + log.debug('No valid compilation database located') return None diff --git a/cmake_pc_hooks/clang_tidy.py b/cmake_pc_hooks/clang_tidy.py index 8e54ed6..6fd8414 100644 --- a/cmake_pc_hooks/clang_tidy.py +++ b/cmake_pc_hooks/clang_tidy.py @@ -32,12 +32,13 @@ def __init__(self, args): self.parse_args(args) self.edit_in_place = '-fix' in self.args or '--fix-errors' in self.args self.handle_ddash_args() + self.log = logging.getLogger(__name__) compile_db = self._resolve_compilation_database(self.cmake.build_dir, self.build_dir_list) if not self.cmake.no_cmake_configure or compile_db: self.add_if_missing([f'-p={compile_db}']) - def _parse_output(self, result): # noqa: PLR6301 + def _parse_output(self, result): """ Parse output and check whether some errors occurred. @@ -51,8 +52,8 @@ def _parse_output(self, result): # noqa: PLR6301 if not result.stdout or 'non-user code' in result.stderr: result.stderr = '' - logging.debug('returncode %d', result.returncode) - logging.debug('parsing output from %s', result.stderr) + self.log.debug('returncode %d', result.returncode) + self.log.debug('parsing output from %s', result.stderr) return result.returncode != 0 or any( msg in result.stderr for msg in ( diff --git a/cmake_pc_hooks/cppcheck.py b/cmake_pc_hooks/cppcheck.py index 0e5ae68..155ff53 100644 --- a/cmake_pc_hooks/cppcheck.py +++ b/cmake_pc_hooks/cppcheck.py @@ -38,6 +38,7 @@ def __init__(self, args): self.add_if_missing(['--error-exitcode=1']) # Enable all of the checks self.add_if_missing(['--enable=all']) + self.log = logging.getLogger(__name__) compile_db = self._resolve_compilation_database(self.cmake.build_dir, self.build_dir_list) if not self.cmake.no_cmake_configure or compile_db: @@ -60,11 +61,11 @@ def _parse_output(self, result): False if no errors were detected, True in all other cases. """ # Useless error see https://stackoverflow.com/questions/6986033 - logging.debug('parsing output from %s', result.stderr) + self.log.debug('parsing output from %s', result.stderr) useless_error_part = 'Cppcheck cannot find all the include files' - result.stderr = ''.join([ - line for line in result.stderr.splitlines(keepends=True) if useless_error_part not in line - ]) + result.stderr = ''.join( + [line for line in result.stderr.splitlines(keepends=True) if useless_error_part not in line] + ) self._clinters_compat() return result.returncode != 0 diff --git a/cmake_pc_hooks/include_what_you_use.py b/cmake_pc_hooks/include_what_you_use.py index 04222ae..378de9e 100644 --- a/cmake_pc_hooks/include_what_you_use.py +++ b/cmake_pc_hooks/include_what_you_use.py @@ -20,6 +20,8 @@ from ._utils import ClangAnalyzerCmd +log = logging.getLogger(__name__) + def get_iwyu_tool_command(iwyu_tool_names=None): """ @@ -35,7 +37,7 @@ def get_iwyu_tool_command(iwyu_tool_names=None): for iwyu_tool in iwyu_tool_names: fname = shutil.which(iwyu_tool) if fname: - logging.debug('found iwyu-tool command at %s', fname) + log.debug('found iwyu-tool command at %s', fname) return fname # Nothing worked -> give up! @@ -56,7 +58,7 @@ def get_iwyu_command(iwyu_names=None): for iwyu in iwyu_names: fname = shutil.which(iwyu) if fname: - logging.debug('found iwyu command at %s', fname) + log.debug('found iwyu command at %s', fname) return fname # Nothing worked -> give up! @@ -86,13 +88,14 @@ def __init__(self, args): self.check_installed() self.parse_args(args) self.handle_ddash_args() + self.log = logging.getLogger(__name__) # Force location of compile database compile_db = self._resolve_compilation_database(self.cmake.build_dir, self.build_dir_list) if compile_db: self.add_if_missing([f'-p={compile_db}']) - def _parse_output(self, result): # noqa: PLR6301 + def _parse_output(self, result): """ Parse output and check whether some errors occurred. @@ -105,7 +108,7 @@ def _parse_output(self, result): # noqa: PLR6301 Notes: Include-What-You-Use return code is never 0 """ - logging.debug('parsing output from %s', result.stdout) + self.log.debug('parsing output from %s', result.stdout) is_correct = 'has correct #includes/fwd-decls' in result.stdout return bool(result.stdout) and not is_correct diff --git a/pyproject.toml b/pyproject.toml index eea96e8..2f5dc3b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -70,56 +70,56 @@ target-version = 'py38' [tool.ruff.lint] -select = ['F', # pyflakes - 'E', # pycodestyle - 'W', # pycodestyle - 'C90', # mccabe - 'I', # isort - 'N', # pep8-naming - 'D', # pydocstyle - 'UP', # pyupgrade - 'YTT', # flake-2020 - 'ANN', # flake8-annotations - 'ASYNC', # flake8-async - 'TRIO', # flake8-trio - 'S', # flake8-bandit - 'BLE', # flake8-blind-except - 'FBT', # flake8-boolean-trap - 'B', # flake8-bugbear - 'A', # flake8-builtins - 'C4', # flake8-comprehensions - 'DTZ', # flake8-datetimez - 'T10', # flake8-debugger - 'EM', # flake8-errmsg - 'EXE', # flake8-executable - 'FA', # flake8-future-annotations - 'ISC', # flake8-implicit-str-concat - 'ICN', # flake8-import-conventions - 'G', # flake8-logging-format - 'PIE', # flake8-pie - 'PT', # flake8-pytest-style - 'Q', # flake8-quotes - 'RSE', # flake8-raise - 'RET', # flake8-return - 'SLF', # flake8-self - 'SLOT', # flake8-slots - 'SIM', # flake8-simplify - 'TID', # flake8-tidy-imports - 'TCH', # flake8-type-checking - 'INT', # flake8-gettext - 'ARG', # flake8-unused-arguments - 'PTH', # flake8-use-pathlib - 'TD', # flake8-todos - 'FIX', # flake8-fixme - 'ERA', # eradicate - 'PL', # pylint - 'TRY', # tryceratops - 'FLY', # flynt - 'NPY', # numpy specific rules - 'PERF', # perflint - 'FURB', # refurb - 'LOG', # flake8-logging - 'RUF', # ruff-specific rules +select = ['F', # pyflakes + 'E', # pycodestyle + 'W', # pycodestyle + 'C90', # mccabe + 'I', # isort + 'N', # pep8-naming + 'D', # pydocstyle + 'UP', # pyupgrade + 'YTT', # flake-2020 + 'ANN', # flake8-annotations + 'ASYNC', # flake8-async + 'ASYNC1', # flake8-trio + 'S', # flake8-bandit + 'BLE', # flake8-blind-except + 'FBT', # flake8-boolean-trap + 'B', # flake8-bugbear + 'A', # flake8-builtins + 'C4', # flake8-comprehensions + 'DTZ', # flake8-datetimez + 'T10', # flake8-debugger + 'EM', # flake8-errmsg + 'EXE', # flake8-executable + 'FA', # flake8-future-annotations + 'ISC', # flake8-implicit-str-concat + 'ICN', # flake8-import-conventions + 'G', # flake8-logging-format + 'PIE', # flake8-pie + 'PT', # flake8-pytest-style + 'Q', # flake8-quotes + 'RSE', # flake8-raise + 'RET', # flake8-return + 'SLF', # flake8-self + 'SLOT', # flake8-slots + 'SIM', # flake8-simplify + 'TID', # flake8-tidy-imports + 'TCH', # flake8-type-checking + 'INT', # flake8-gettext + 'ARG', # flake8-unused-arguments + 'PTH', # flake8-use-pathlib + 'TD', # flake8-todos + 'FIX', # flake8-fixme + 'ERA', # eradicate + 'PL', # pylint + 'TRY', # tryceratops + 'FLY', # flynt + 'NPY', # numpy specific rules + 'PERF', # perflint + 'FURB', # refurb + 'LOG', # flake8-logging + 'RUF', # ruff-specific rules ] ignore = ['ANN101', # missing-type-self diff --git a/setup.py b/setup.py index ea75600..f0b3440 100644 --- a/setup.py +++ b/setup.py @@ -26,6 +26,8 @@ from setuptools import setup from setuptools.command.egg_info import egg_info +log = logging.getLogger(__name__) + def _try_calling_executable(exec_cmd: list[str | Path]) -> bool: """ @@ -58,7 +60,7 @@ def get_executable(exec_name): exec_name = Path(exec_name).name - logging.info('trying to locate %s in %s', exec_name, root_path) + log.info('trying to locate %s in %s', exec_name, root_path) search_paths = [root_path, root_path / 'bin', root_path / 'Scripts'] @@ -66,20 +68,20 @@ def get_executable(exec_name): for base_path in search_paths: cmd = [base_path / exec_name] if _try_calling_executable(cmd): - logging.info(' command found: %s', cmd) + log.info(' command found: %s', cmd) return cmd - logging.info(' failed in %s', base_path) + log.info(' failed in %s', base_path) # That did not work: try calling it through Python for base_path in search_paths: cmd = [python, base_path / exec_name] if _try_calling_executable(cmd): - logging.info(' command found: %s', cmd) + log.info(' command found: %s', cmd) return cmd - logging.info(' failed in %s', base_path) + log.info(' failed in %s', base_path) - logging.info(' command *not* found in virtualenv!') + log.info(' command *not* found in virtualenv!') return shutil.which(exec_name) diff --git a/tests/python/_argparse_test.py b/tests/python/_argparse_test.py index 64ff6c1..754f50b 100644 --- a/tests/python/_argparse_test.py +++ b/tests/python/_argparse_test.py @@ -35,7 +35,7 @@ def _add_simple_args(parser): # ------------------------------------------------------------------------------ -@pytest.fixture() +@pytest.fixture def simple_toml_content(): return dedent( """ diff --git a/tests/python/_cmake_test.py b/tests/python/_cmake_test.py index 15a8cfb..d81e8c0 100644 --- a/tests/python/_cmake_test.py +++ b/tests/python/_cmake_test.py @@ -73,8 +73,16 @@ def test_cmake_command_init(): ([], 'automatic_discovery', True), (['--no-automatic-discovery'], 'automatic_discovery', False), (['--detect-configured-files'], 'detect_configured_files', True), - (['--linux="-DCMAKE_CXX_COMPILER=g++"'], 'linux', ['"-DCMAKE_CXX_COMPILER=g++"']), - (['--mac="-DCMAKE_CXX_COMPILER=clang++"'], 'mac', ['"-DCMAKE_CXX_COMPILER=clang++"']), + ( + ['--linux="-DCMAKE_CXX_COMPILER=g++"'], + 'linux', + ['"-DCMAKE_CXX_COMPILER=g++"'], + ), + ( + ['--mac="-DCMAKE_CXX_COMPILER=clang++"'], + 'mac', + ['"-DCMAKE_CXX_COMPILER=clang++"'], + ), (['--win="-DCMAKE_CXX_COMPILER=cl"'], 'win', ['"-DCMAKE_CXX_COMPILER=cl"']), # CMake-like options (['-S/path/to/source'], 'source_dir', '/path/to/source'), @@ -118,7 +126,15 @@ def test_cmake_parser_unix_platform_setup(parser): (None, ['gcc-build/CMakeCache.txt', 'build/CMakeCache.txt'], 'build'), (['clang', 'gcc'], ['gcc-build/compile_commands.json'], 'clang'), (['clang'], ['gcc-build/CMakeCache.txt'], 'gcc-build'), - (['clang'], ['gcc-build/CMakeCache.txt', 'clang/CMakeCache.txt', 'build/CMakeCache.txt'], 'clang'), + ( + ['clang'], + [ + 'gcc-build/CMakeCache.txt', + 'clang/CMakeCache.txt', + 'build/CMakeCache.txt', + ], + 'clang', + ), ], ) def test_resolve_build_directory(tmp_path, dir_list, build_dir_tree, ref_path): @@ -135,20 +151,24 @@ def test_resolve_build_directory(tmp_path, dir_list, build_dir_tree, ref_path): assert cmake.build_dir is None cmake.resolve_build_directory( - None if dir_list is None else [tmp_path / path for path in dir_list], automatic_discovery=True + None if dir_list is None else [tmp_path / path for path in dir_list], + automatic_discovery=True, ) assert cmake.build_dir == tmp_path / ref_path cmake.build_dir = None cmake.resolve_build_directory( - None if dir_list is None else [tmp_path / path for path in dir_list], automatic_discovery=False + None if dir_list is None else [tmp_path / path for path in dir_list], + automatic_discovery=False, ) assert cmake.build_dir == tmp_path / cmake.DEFAULT_BUILD_DIR if dir_list is None else dir_list[0] @pytest.mark.parametrize('system', ['Linux', 'Darwin', 'Windows']) @pytest.mark.parametrize('no_cmake_configure', [False, True]) -def test_setup_cmake_args(mocker, system, no_cmake_configure): # noqa: PLR0915, PLR0912, C901 +def test_setup_cmake_args( # noqa: PLR0912, PLR0915, C901 + mocker, system, no_cmake_configure +): original_system = platform.system() def system_stub(): @@ -232,14 +252,23 @@ def system_stub(): @pytest.mark.parametrize('returncode', [0, 1]) @pytest.mark.parametrize('clean_build', [False, True]) @pytest.mark.parametrize('no_cmake_configure', [False, True]) -def test_configure_cmake(mocker, tmp_path, clean_build, returncode, no_cmake_configure, detect_configured_files): # noqa: PLR0917 +def test_configure_cmake( # noqa: PLR0917 + mocker, + tmp_path, + clean_build, + returncode, + no_cmake_configure, + detect_configured_files, +): sys_exit = mocker.patch('sys.exit') FileLock = mocker.MagicMock(filelock.FileLock) # noqa: N806 mocker.patch(filelock_module_name, FileLock) - InterProcessReaderWriterLock = mocker.MagicMock(fasteners.InterProcessReaderWriterLock) # noqa: N806 + InterProcessReaderWriterLock = mocker.MagicMock( # noqa: N806 + fasteners.InterProcessReaderWriterLock + ) mocker.patch(interprocess_rw_lock_module_name, InterProcessReaderWriterLock) - _configure = mocker.Mock(return_value=returncode) - mocker.patch(internal_cmake_configure_name, _configure) + configure = mocker.Mock(return_value=returncode) + mocker.patch(internal_cmake_configure_name, configure) parse_log = mocker.patch('cmake_pc_hooks._cmake.CMakeCommand._parse_cmake_trace_log') # ---------------------------------- @@ -262,13 +291,17 @@ def test_configure_cmake(mocker, tmp_path, clean_build, returncode, no_cmake_con if no_cmake_configure: FileLock.assert_not_called() InterProcessReaderWriterLock.assert_not_called() - _configure.assert_not_called() + configure.assert_not_called() return FileLock.assert_called_once_with(build_dir / '_cmake_configure_try_lock') InterProcessReaderWriterLock.assert_called_once_with(build_dir / '_cmake_configure_lock') - _configure.assert_called_once_with( - lock_files=(InterProcessReaderWriterLock.call_args[0][0], FileLock.call_args[0][0]), clean_build=clean_build + configure.assert_called_once_with( + lock_files=( + InterProcessReaderWriterLock.call_args[0][0], + FileLock.call_args[0][0], + ), + clean_build=clean_build, ) if detect_configured_files: @@ -295,10 +328,12 @@ def timeout(blocking): # noqa: ARG001 mocker.patch(filelock_module_name, FileLock) interprocess_lock = mocker.MagicMock() - InterProcessReaderWriterLock = mocker.MagicMock(return_value=interprocess_lock) # noqa: N806 + InterProcessReaderWriterLock = mocker.MagicMock( # noqa: N806 + return_value=interprocess_lock + ) mocker.patch(interprocess_rw_lock_module_name, InterProcessReaderWriterLock) - _configure = mocker.Mock(return_value=0) - mocker.patch(internal_cmake_configure_name, _configure) + configure = mocker.Mock(return_value=0) + mocker.patch(internal_cmake_configure_name, configure) # ---------------------------------- @@ -317,7 +352,7 @@ def timeout(blocking): # noqa: ARG001 FileLock.assert_called_once() InterProcessReaderWriterLock.assert_called_once() interprocess_lock.read_lock.assert_called_once() - _configure.assert_not_called() + configure.assert_not_called() def test_configure_invalid(mocker): @@ -325,10 +360,12 @@ def test_configure_invalid(mocker): FileLock = mocker.MagicMock(filelock.FileLock) # noqa: N806 mocker.patch(filelock_module_name, FileLock) - InterProcessReaderWriterLock = mocker.MagicMock(fasteners.InterProcessReaderWriterLock) # noqa: N806 + InterProcessReaderWriterLock = mocker.MagicMock( # noqa: N806 + fasteners.InterProcessReaderWriterLock + ) mocker.patch(interprocess_rw_lock_module_name, InterProcessReaderWriterLock) - _configure = mocker.Mock(return_value=1) - mocker.patch(internal_cmake_configure_name, _configure) + configure = mocker.Mock(return_value=1) + mocker.patch(internal_cmake_configure_name, configure) # ---------------------------------- @@ -343,7 +380,8 @@ def test_configure_invalid(mocker): def test_configure_cmake_internal(mocker, tmp_path, clean_build, detect_configured_files): mocker.patch('shutil.rmtree') call_cmake = mocker.patch( - 'cmake_pc_hooks._cmake.CMakeCommand._call_cmake', return_value=mocker.Mock(stdout='', stderr='', returncode=0) + 'cmake_pc_hooks._cmake.CMakeCommand._call_cmake', + return_value=mocker.Mock(stdout='', stderr='', returncode=0), ) # ---------------------------------- @@ -396,7 +434,8 @@ def test_configure_cmake_internal(mocker, tmp_path, clean_build, detect_configur def test_call_cmake(mocker, tmp_path): call_process = mocker.patch( - 'cmake_pc_hooks._call_process.call_process', return_value=mocker.Mock(stdout='', stderr='', returncode=0) + 'cmake_pc_hooks._call_process.call_process', + return_value=mocker.Mock(stdout='', stderr='', returncode=0), ) # ---------------------------------- @@ -412,13 +451,15 @@ def test_call_cmake(mocker, tmp_path): cmake._call_cmake() call_process.assert_called_once_with( - [*cmake.command, str(cmake.source_dir), *cmake.cmake_args], cwd=str(cmake.build_dir) + [*cmake.command, str(cmake.source_dir), *cmake.cmake_args], + cwd=str(cmake.build_dir), ) extra_args = ['-N', '-LA'] cmake._call_cmake(extra_args=extra_args) call_process.assert_called_with( - [*cmake.command, str(cmake.source_dir), *cmake.cmake_args, *extra_args], cwd=str(cmake.build_dir) + [*cmake.command, str(cmake.source_dir), *cmake.cmake_args, *extra_args], + cwd=str(cmake.build_dir), ) diff --git a/tests/python/conftest.py b/tests/python/conftest.py index fa79e4a..594b5b5 100644 --- a/tests/python/conftest.py +++ b/tests/python/conftest.py @@ -8,7 +8,7 @@ # ============================================================================== -@pytest.fixture() +@pytest.fixture def compile_commands(tmp_path): path = tmp_path / 'build' / 'compile_commands.json' @@ -22,11 +22,13 @@ def compile_commands(tmp_path): for fname in file_list: fname.parent.mkdir(parents=True, exist_ok=True) fname.write_text('') - data.append({ - 'directory': str(fname.parent), - 'file': str(fname), - 'command': f'/usr/bin/c++ -DONE -DTWO -Wall -c {fname}', - }) + data.append( + { + 'directory': str(fname.parent), + 'file': str(fname), + 'command': f'/usr/bin/c++ -DONE -DTWO -Wall -c {fname}', + } + ) path.parent.mkdir(parents=True, exist_ok=True) with path.open(mode='w', encoding='utf-8') as fd: