From 6df4330714710293975f09786d32bd95e9825485 Mon Sep 17 00:00:00 2001 From: Sergei Izmailov Date: Sat, 21 Oct 2023 17:03:41 +0900 Subject: [PATCH 1/3] fix: Show path to method/function in error message --- .../parser/mixins/error_handlers.py | 58 ++++++++++++------- 1 file changed, 36 insertions(+), 22 deletions(-) diff --git a/pybind11_stubgen/parser/mixins/error_handlers.py b/pybind11_stubgen/parser/mixins/error_handlers.py index 3d44c71..ef7f538 100644 --- a/pybind11_stubgen/parser/mixins/error_handlers.py +++ b/pybind11_stubgen/parser/mixins/error_handlers.py @@ -12,45 +12,59 @@ ParserError, ) from pybind11_stubgen.parser.interface import IParser -from pybind11_stubgen.structs import Class, Module, QualifiedName +from pybind11_stubgen.structs import Class, Function, Method, Module, QualifiedName + + +class LocalErrors: + def __init__(self, path: QualifiedName, errors: set[str], stack: list[LocalErrors]): + self.path: QualifiedName = path + self.errors: set[str] = errors + self._stack: list[LocalErrors] = stack + + def __enter__(self) -> LocalErrors: + self._stack.append(self) + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + top = self._stack.pop() + assert top == self class LoggerData(IParser): def __init__(self): super().__init__() - self._seen_errors: set[str] = set() - self.__current_path: QualifiedName | None = None + self.stack: list[LocalErrors] = [] + + def __new_layer(self, path: QualifiedName) -> LocalErrors: + return LocalErrors(path, errors=set(), stack=self.stack) def handle_module( self, path: QualifiedName, module: types.ModuleType ) -> Module | None: - old_errors = self._seen_errors - old_module = self.__current_path - self._seen_errors = set() - self.__current_path = path - result = super().handle_module(path, module) - self._seen_errors = old_errors - self.__current_path = old_module - return result + with self.__new_layer(path): + return super().handle_module(path, module) def handle_class(self, path: QualifiedName, class_: type) -> Class | None: - old_errors = self._seen_errors - old_module = self.__current_path - self._seen_errors = set() - self.__current_path = path - result = super().handle_class(path, class_) - self._seen_errors = old_errors - self.__current_path = old_module - return result + with self.__new_layer(path): + return super().handle_class(path, class_) + + def handle_function(self, path: QualifiedName, class_: type) -> list[Function]: + with self.__new_layer(path): + return super().handle_function(path, class_) + + def handle_method(self, path: QualifiedName, class_: type) -> list[Method]: + with self.__new_layer(path): + return super().handle_method(path, class_) @property def current_path(self) -> QualifiedName: - assert self.__current_path is not None - return self.__current_path + assert len(self.stack) != 0 + return self.stack[-1].path @property def reported_errors(self) -> set[str]: - return self._seen_errors + assert len(self.stack) != 0 + return self.stack[-1].errors logger = getLogger("pybind11_stubgen") From e109fcaeda0958742975a70c92185889460fc0a0 Mon Sep 17 00:00:00 2001 From: Sergei Izmailov Date: Sat, 21 Oct 2023 17:21:28 +0900 Subject: [PATCH 2/3] tests: Test `pybind11-stubgen demo` stderr --- .github/workflows/ci.yml | 4 +++ tests/check-demo-errors-generation.sh | 45 +++++++++++++++++++++++++++ tests/demo.errors.stderr.txt | 15 +++++++++ 3 files changed, 64 insertions(+) create mode 100755 tests/check-demo-errors-generation.sh create mode 100644 tests/demo.errors.stderr.txt diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1fc163d..88a40c8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -86,6 +86,10 @@ jobs: path: "./tests/stubs/python-${{ matrix.python }}/pybind11-${{ matrix.pybind11-branch }}.patch" retention-days: 30 if-no-files-found: ignore + - name: Check error generation + shell: bash + run: ./tests/check-demo-errors-generation.sh + test-cli-options: name: "Runs on 🐍 ${{ matrix.python }} • ${{ matrix.test-package }}" runs-on: ubuntu-latest diff --git a/tests/check-demo-errors-generation.sh b/tests/check-demo-errors-generation.sh new file mode 100755 index 0000000..71906c7 --- /dev/null +++ b/tests/check-demo-errors-generation.sh @@ -0,0 +1,45 @@ +#!/bin/bash + +set -e + +TESTS_ROOT="$(readlink -m "$(dirname "$0")")" +DEMO_ERRORS_FILE="${TESTS_ROOT}/demo.errors.stderr.txt" +STUBS_DIR="/tmp/out" # Stubs should never be actually written + +remove_demo_errors() { + rm -rf "${DEMO_ERRORS_FILE}"; +} + +check_error_messages() { + ( + set -o pipefail ; + git diff --exit-code HEAD -- "${DEMO_ERRORS_FILE}"; + ) +} +run_stubgen() { + ( + set +e ; + pybind11-stubgen \ + demo \ + --output-dir=${STUBS_DIR} \ + --exit-code \ + 2> "${DEMO_ERRORS_FILE}" \ + || exit 0 + ) || ( + echo "'pybind11-stubgen demo --exit-code' did not exit with code 1" + exit 1 + ) +} + +remove_randomness_in_errors (){ + sed -i 's/0x[0-9a-f]*/0x1234abcd5678/gi' "${DEMO_ERRORS_FILE}" +} + +main () { + remove_demo_errors + run_stubgen + remove_randomness_in_errors + check_error_messages +} + +main "$@" diff --git a/tests/demo.errors.stderr.txt b/tests/demo.errors.stderr.txt new file mode 100644 index 0000000..e858fa4 --- /dev/null +++ b/tests/demo.errors.stderr.txt @@ -0,0 +1,15 @@ +pybind11_stubgen - [ ERROR] In demo._bindings.aliases.foreign_enum_default : Invalid expression '' +pybind11_stubgen - [ ERROR] In demo._bindings.enum.accept_defaulted_enum : Invalid expression '' +pybind11_stubgen - [ ERROR] In demo._bindings.flawed_bindings.accept_unbound_enum : Invalid expression '(anonymous namespace)::Enum' +pybind11_stubgen - [ ERROR] In demo._bindings.flawed_bindings.accept_unbound_enum_defaulted : Invalid expression '' +pybind11_stubgen - [ ERROR] In demo._bindings.flawed_bindings.accept_unbound_type : Invalid expression '(anonymous namespace)::Unbound' +pybind11_stubgen - [ ERROR] In demo._bindings.flawed_bindings.accept_unbound_type_defaulted : Invalid expression '' +pybind11_stubgen - [ ERROR] In demo._bindings.flawed_bindings.get_unbound_type : Invalid expression '(anonymous namespace)::Unbound' +pybind11_stubgen - [WARNING] Enum-like str representations were found with no matching mapping to the enum class location. +Use `--enum-class-locations` to specify full path to the following enum(s): + - ConsoleForegroundColor +pybind11_stubgen - [WARNING] Raw C++ types/values were found in signatures extracted from docstrings. +Please check the corresponding sections of pybind11 documentation to avoid common mistakes in binding code: + - https://pybind11.readthedocs.io/en/latest/advanced/misc.html#avoiding-cpp-types-in-docstrings + - https://pybind11.readthedocs.io/en/latest/advanced/functions.html#default-arguments-revisited +pybind11_stubgen - [ INFO] Terminating due to previous errors From bdabccee2fbff3123bbab9fed9c50674cf54f946 Mon Sep 17 00:00:00 2001 From: Sergei Izmailov Date: Sat, 21 Oct 2023 17:51:40 +0900 Subject: [PATCH 3/3] ci: Skip 3.7/3.8 stderr check - Use of `typing.Annotated` produces an extra warning in <3.9 --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 88a40c8..cfb76b5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -86,7 +86,9 @@ jobs: path: "./tests/stubs/python-${{ matrix.python }}/pybind11-${{ matrix.pybind11-branch }}.patch" retention-days: 30 if-no-files-found: ignore + - name: Check error generation + if: ${{ !contains(fromJson('["3.7", "3.8"]'), matrix.python) }} shell: bash run: ./tests/check-demo-errors-generation.sh