From 0986af61824498fbc17aee7c9caae4c40dfd12d8 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 15 Feb 2022 11:27:00 -0500 Subject: [PATCH 1/3] [pre-commit.ci] pre-commit autoupdate (#3672) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/Lucas-C/pre-commit-hooks: v1.1.11 → v1.1.12](https://github.com/Lucas-C/pre-commit-hooks/compare/v1.1.11...v1.1.12) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 564d50042..8ab7117fe 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -59,7 +59,7 @@ repos: # Changes tabs to spaces - repo: https://github.com/Lucas-C/pre-commit-hooks - rev: "v1.1.11" + rev: "v1.1.12" hooks: - id: remove-tabs From c14170a7878e7ac2f88e11657d629026d1ca31f3 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Tue, 15 Feb 2022 11:51:17 -0800 Subject: [PATCH 2/3] Removing `// clang-format off` - `on` directives from test_pickling.cpp (#3738) --- tests/test_pickling.cpp | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/tests/test_pickling.cpp b/tests/test_pickling.cpp index bf1ee4671..9787bd300 100644 --- a/tests/test_pickling.cpp +++ b/tests/test_pickling.cpp @@ -1,4 +1,3 @@ -// clang-format off /* tests/test_pickling.cpp -- pickle support @@ -11,8 +10,6 @@ #include "pybind11_tests.h" -// clang-format on - #include #include #include @@ -63,19 +60,18 @@ void wrap(py::module m) { } // namespace exercise_trampoline -// clang-format off - TEST_SUBMODULE(pickling, m) { // test_roundtrip class Pickleable { public: - explicit Pickleable(const std::string &value) : m_value(value) { } + explicit Pickleable(const std::string &value) : m_value(value) {} const std::string &value() const { return m_value; } void setExtra1(int extra1) { m_extra1 = extra1; } void setExtra2(int extra2) { m_extra2 = extra2; } int extra1() const { return m_extra1; } int extra2() const { return m_extra2; } + private: std::string m_value; int m_extra1 = 0; @@ -88,8 +84,7 @@ TEST_SUBMODULE(pickling, m) { }; py::class_ pyPickleable(m, "Pickleable"); - pyPickleable - .def(py::init()) + pyPickleable.def(py::init()) .def("value", &Pickleable::value) .def("extra1", &Pickleable::extra1) .def("extra2", &Pickleable::extra2) @@ -105,7 +100,7 @@ TEST_SUBMODULE(pickling, m) { pyPickleable.def("__setstate__", [](Pickleable &p, const py::tuple &t) { if (t.size() != 3) { throw std::runtime_error("Invalid state!"); -} + } /* Invoke the constructor (need to use in-place version) */ new (&p) Pickleable(t[0].cast()); @@ -124,7 +119,7 @@ TEST_SUBMODULE(pickling, m) { [](const py::tuple &t) { if (t.size() != 3) { throw std::runtime_error("Invalid state!"); -} + } auto p = PickleableNew(t[0].cast()); p.setExtra1(t[1].cast()); @@ -136,7 +131,7 @@ TEST_SUBMODULE(pickling, m) { // test_roundtrip_with_dict class PickleableWithDict { public: - explicit PickleableWithDict(const std::string &value) : value(value) { } + explicit PickleableWithDict(const std::string &value) : value(value) {} std::string value; int extra; @@ -147,7 +142,8 @@ TEST_SUBMODULE(pickling, m) { using PickleableWithDict::PickleableWithDict; }; - py::class_ pyPickleableWithDict(m, "PickleableWithDict", py::dynamic_attr()); + py::class_ pyPickleableWithDict( + m, "PickleableWithDict", py::dynamic_attr()); pyPickleableWithDict.def(py::init()) .def_readwrite("value", &PickleableWithDict::value) .def_readwrite("extra", &PickleableWithDict::extra) @@ -159,7 +155,7 @@ TEST_SUBMODULE(pickling, m) { pyPickleableWithDict.def("__setstate__", [](const py::object &self, const py::tuple &t) { if (t.size() != 3) { throw std::runtime_error("Invalid state!"); -} + } /* Cast and construct */ auto &p = self.cast(); new (&p) PickleableWithDict(t[0].cast()); @@ -176,12 +172,13 @@ TEST_SUBMODULE(pickling, m) { .def(py::init()) .def(py::pickle( [](const py::object &self) { - return py::make_tuple(self.attr("value"), self.attr("extra"), self.attr("__dict__")); + return py::make_tuple( + self.attr("value"), self.attr("extra"), self.attr("__dict__")); }, [](const py::tuple &t) { if (t.size() != 3) { throw std::runtime_error("Invalid state!"); -} + } auto cpp_state = PickleableWithDictNew(t[0].cast()); cpp_state.extra = t[1].cast(); From 4b42c3719113a613ad48b2e9dab218fb18f13f8a Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Tue, 15 Feb 2022 17:48:33 -0500 Subject: [PATCH 3/3] style: pylint (#3720) * fix: add pylint and fix issue * chore: add pylint to pre-commit * fix: local variable warning surfaced mistake in intree --- .gitattributes | 1 + .github/matchers/pylint.json | 32 +++++++++++++++++++++++++++ .github/workflows/format.yml | 5 +++++ .pre-commit-config.yaml | 27 +++++++++++++--------- noxfile.py | 9 ++++++-- pybind11/__main__.py | 2 ++ pybind11/commands.py | 15 +++++++++---- pybind11/setup_helpers.py | 43 ++++++++++++++++++++---------------- pyproject.toml | 27 ++++++++++++++++++++++ 9 files changed, 126 insertions(+), 35 deletions(-) create mode 100644 .gitattributes create mode 100644 .github/matchers/pylint.json diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..d611e1496 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +docs/*.svg binary diff --git a/.github/matchers/pylint.json b/.github/matchers/pylint.json new file mode 100644 index 000000000..e3a6bd16b --- /dev/null +++ b/.github/matchers/pylint.json @@ -0,0 +1,32 @@ +{ + "problemMatcher": [ + { + "severity": "warning", + "pattern": [ + { + "regexp": "^([^:]+):(\\d+):(\\d+): ([A-DF-Z]\\d+): \\033\\[[\\d;]+m([^\\033]+).*$", + "file": 1, + "line": 2, + "column": 3, + "code": 4, + "message": 5 + } + ], + "owner": "pylint-warning" + }, + { + "severity": "error", + "pattern": [ + { + "regexp": "^([^:]+):(\\d+):(\\d+): (E\\d+): \\033\\[[\\d;]+m([^\\033]+).*$", + "file": 1, + "line": 2, + "column": 3, + "code": 4, + "message": 5 + } + ], + "owner": "pylint-error" + } + ] +} diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index ab7b40503..46140fe0c 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -12,6 +12,9 @@ on: - stable - "v*" +env: + FORCE_COLOR: 3 + jobs: pre-commit: name: Format @@ -19,6 +22,8 @@ jobs: steps: - uses: actions/checkout@v2 - uses: actions/setup-python@v2 + - name: Add matchers + run: echo "::add-matcher::$GITHUB_WORKSPACE/.github/matchers/pylint.json" - uses: pre-commit/action@v2.0.3 with: # Slow hooks are marked with manual - slow is okay here, run them too diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8ab7117fe..0ff02f511 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -87,23 +87,30 @@ repos: - id: rst-directive-colons - id: rst-inline-touching-normal -# Flake8 also supports pre-commit natively (same author) -- repo: https://github.com/PyCQA/flake8 - rev: "4.0.1" - hooks: - - id: flake8 - additional_dependencies: &flake8_dependencies - - flake8-bugbear - - pep8-naming - exclude: ^(docs/.*|tools/.*)$ - # Automatically remove noqa that are not used - repo: https://github.com/asottile/yesqa rev: "v1.3.0" hooks: - id: yesqa + additional_dependencies: &flake8_dependencies + - flake8-bugbear + - pep8-naming + +# Flake8 also supports pre-commit natively (same author) +- repo: https://github.com/PyCQA/flake8 + rev: "4.0.1" + hooks: + - id: flake8 + exclude: ^(docs/.*|tools/.*)$ additional_dependencies: *flake8_dependencies +# PyLint has native support - not always usable, but works for us +- repo: https://github.com/PyCQA/pylint + rev: "v2.12.2" + hooks: + - id: pylint + files: ^pybind11 + # CMake formatting - repo: https://github.com/cheshirekow/cmake-format-precommit rev: "v0.6.13" diff --git a/noxfile.py b/noxfile.py index 4b6adcfbd..50b7e4ee0 100644 --- a/noxfile.py +++ b/noxfile.py @@ -1,8 +1,13 @@ +import os + import nox nox.options.sessions = ["lint", "tests", "tests_packaging"] -PYTHON_VERSIONS = ["3.6", "3.7", "3.8", "3.9", "3.10", "3.11"] +PYTHON_VERISONS = ["3.6", "3.7", "3.8", "3.9", "3.10", "3.11", "pypy3.7", "pypy3.8"] + +if os.environ.get("CI", None): + nox.options.error_on_missing_interpreters = True @nox.session(reuse_venv=True) @@ -14,7 +19,7 @@ def lint(session: nox.Session) -> None: session.run("pre-commit", "run", "-a") -@nox.session(python=PYTHON_VERSIONS) +@nox.session(python=PYTHON_VERISONS) def tests(session: nox.Session) -> None: """ Run the tests (requires a compiler). diff --git a/pybind11/__main__.py b/pybind11/__main__.py index 645c76042..22fc4471e 100644 --- a/pybind11/__main__.py +++ b/pybind11/__main__.py @@ -1,3 +1,5 @@ +# pylint: disable=missing-function-docstring + import argparse import sys import sysconfig diff --git a/pybind11/commands.py b/pybind11/commands.py index 84d41478a..a29c8ca61 100644 --- a/pybind11/commands.py +++ b/pybind11/commands.py @@ -3,16 +3,23 @@ import os DIR = os.path.abspath(os.path.dirname(__file__)) -def get_include(user: bool = False) -> str: +def get_include(user: bool = False) -> str: # pylint: disable=unused-argument + """ + Return the path to the pybind11 include directory. The historical "user" + argument is unused, and may be removed. + """ installed_path = os.path.join(DIR, "include") source_path = os.path.join(os.path.dirname(DIR), "include") return installed_path if os.path.exists(installed_path) else source_path def get_cmake_dir() -> str: + """ + Return the path to the pybind11 CMake module directory. + """ cmake_installed_path = os.path.join(DIR, "share", "cmake", "pybind11") if os.path.exists(cmake_installed_path): return cmake_installed_path - else: - msg = "pybind11 not installed, installation required to access the CMake files" - raise ImportError(msg) + + msg = "pybind11 not installed, installation required to access the CMake files" + raise ImportError(msg) diff --git a/pybind11/setup_helpers.py b/pybind11/setup_helpers.py index e3271ca60..1fd04b915 100644 --- a/pybind11/setup_helpers.py +++ b/pybind11/setup_helpers.py @@ -239,7 +239,7 @@ def has_flag(compiler: Any, flag: str) -> bool: with tmp_chdir(): fname = Path("flagcheck.cpp") # Don't trigger -Wunused-parameter. - fname.write_text("int main (int, char **) { return 0; }") + fname.write_text("int main (int, char **) { return 0; }", encoding="utf-8") try: compiler.compile([str(fname)], extra_postargs=[flag]) @@ -303,29 +303,31 @@ def intree_extensions( """ exts = [] - for path in paths: - if package_dir is None: + if package_dir is None: + for path in paths: parent, _ = os.path.split(path) while os.path.exists(os.path.join(parent, "__init__.py")): parent, _ = os.path.split(parent) relname, _ = os.path.splitext(os.path.relpath(path, parent)) qualified_name = relname.replace(os.path.sep, ".") exts.append(Pybind11Extension(qualified_name, [path])) - else: - for prefix, parent in package_dir.items(): - if path.startswith(parent): - relname, _ = os.path.splitext(os.path.relpath(path, parent)) - qualified_name = relname.replace(os.path.sep, ".") - if prefix: - qualified_name = prefix + "." + qualified_name - exts.append(Pybind11Extension(qualified_name, [path])) + return exts - if not exts: - msg = ( - f"path {path} is not a child of any of the directories listed " - f"in 'package_dir' ({package_dir})" - ) - raise ValueError(msg) + for path in paths: + for prefix, parent in package_dir.items(): + if path.startswith(parent): + relname, _ = os.path.splitext(os.path.relpath(path, parent)) + qualified_name = relname.replace(os.path.sep, ".") + if prefix: + qualified_name = prefix + "." + qualified_name + exts.append(Pybind11Extension(qualified_name, [path])) + break + else: + msg = ( + f"path {path} is not a child of any of the directories listed " + f"in 'package_dir' ({package_dir})" + ) + raise ValueError(msg) return exts @@ -339,7 +341,7 @@ def naive_recompile(obj: str, src: str) -> bool: return os.stat(obj).st_mtime < os.stat(src).st_mtime -def no_recompile(obg: str, src: str) -> bool: +def no_recompile(obg: str, src: str) -> bool: # pylint: disable=unused-argument """ This is the safest but slowest choice (and is the default) - will always recompile sources. @@ -412,7 +414,7 @@ class ParallelCompile: self, envvar: Optional[str] = None, default: int = 0, - max: int = 0, + max: int = 0, # pylint: disable=redefined-builtin needs_recompile: Callable[[str, str], bool] = no_recompile, ) -> None: self.envvar = envvar @@ -488,6 +490,9 @@ class ParallelCompile: return compile_function def install(self: S) -> S: + """ + Installs the compile function into distutils.ccompiler.CCompiler.compile. + """ distutils.ccompiler.CCompiler.compile = self.function() # type: ignore[assignment] return self diff --git a/pyproject.toml b/pyproject.toml index 31fff8bd6..f80cdc1f4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,3 +30,30 @@ strict = true [[tool.mypy.overrides]] module = ["ghapi.*", "setuptools.*"] ignore_missing_imports = true + + +[tool.pytest.ini_options] +minversion = "6.0" +addopts = ["-ra", "--showlocals", "--strict-markers", "--strict-config"] +xfail_strict = true +filterwarnings = ["error"] +log_cli_level = "info" +testpaths = [ + "tests", +] +timeout=300 + + +[tool.pylint] +master.py-version = "3.6" +reports.output-format = "colorized" +messages_control.disable = [ + "design", + "fixme", + "imports", + "line-too-long", + "imports", + "invalid-name", + "protected-access", + "missing-module-docstring", +]