Merge branch 'master' into sh_merge_master

This commit is contained in:
Ralf W. Grosse-Kunstleve 2022-02-16 07:15:34 -08:00
commit bc2cec0783
15 changed files with 162 additions and 69 deletions

1
.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
docs/*.svg binary

32
.github/matchers/pylint.json vendored Normal file
View File

@ -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"
}
]
}

View File

@ -713,7 +713,7 @@ jobs:
include:
- python: 3.9
args: -DCMAKE_CXX_STANDARD=20 -DDOWNLOAD_EIGEN=OFF
args: -DCMAKE_CXX_STANDARD=20
- python: 3.8
args: -DCMAKE_CXX_STANDARD=17
@ -836,7 +836,7 @@ jobs:
cmake -S . -B build
-DPYBIND11_WERROR=ON
-DDOWNLOAD_CATCH=ON
-DDOWNLOAD_EIGEN=OFF
-DDOWNLOAD_EIGEN=ON
-DCMAKE_CXX_STANDARD=20
- name: Build C++20

View File

@ -13,6 +13,9 @@ on:
- smart_holder
- "v*"
env:
FORCE_COLOR: 3
jobs:
pre-commit:
name: Format
@ -20,6 +23,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

View File

@ -60,7 +60,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
exclude: (^docs/.*|\.patch)?$
@ -90,23 +90,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"

View File

@ -822,7 +822,9 @@ struct is_template_base_of_impl {
/// Check if a template is the base of a type. For example:
/// `is_template_base_of<Base, T>` is true if `struct T : Base<U> {}` where U can be anything
template <template <typename...> class Base, typename T>
#if defined(PYBIND11_CPP20) || !defined(_MSC_VER) // Sadly, all MSVC versions incl. 2022 need this.
// Sadly, all MSVC versions incl. 2022 need the workaround, even in C++20 mode.
// See also: https://github.com/pybind/pybind11/pull/3741
#if !defined(_MSC_VER)
using is_template_base_of
= decltype(is_template_base_of_impl<Base>::check((intrinsic_t<T> *) nullptr));
#else

View File

@ -21,10 +21,12 @@
// make it version specific, or even remove it later, but considering that
// 1. C4127 is generally far more distracting than useful for modern template code, and
// 2. we definitely want to ignore any MSVC warnings originating from Eigen code,
// it is probably best to keep this around indefinitely.
// it is probably best to keep this around indefinitely.
#if defined(_MSC_VER)
# pragma warning(push)
# pragma warning(disable : 4127) // C4127: conditional expression is constant
# pragma warning(disable : 5054) // https://github.com/pybind/pybind11/pull/3741
// C5054: operator '&': deprecated between enumerations of different types
#endif
#include <Eigen/Core>

View File

@ -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).

View File

@ -1,3 +1,5 @@
# pylint: disable=missing-function-docstring
import argparse
import sys
import sysconfig

View File

@ -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)

View File

@ -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

View File

@ -1,5 +1,5 @@
[build-system]
requires = ["setuptools>=42", "wheel", "cmake>=3.18", "ninja"]
requires = ["setuptools>=42", "cmake>=3.18", "ninja"]
build-backend = "setuptools.build_meta"
[tool.check-manifest]
@ -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",
]

View File

@ -115,19 +115,19 @@ def test_build_sdist(monkeypatch, tmpdir):
out = subprocess.check_output(
[
sys.executable,
"setup.py",
"sdist",
"--formats=tar",
"--dist-dir",
"-m",
"build",
"--sdist",
"--outdir",
str(tmpdir),
]
)
if hasattr(out, "decode"):
out = out.decode()
(sdist,) = tmpdir.visit("*.tar")
(sdist,) = tmpdir.visit("*.tar.gz")
with tarfile.open(str(sdist)) as tar:
with tarfile.open(str(sdist), "r:gz") as tar:
start = tar.getnames()[0] + "/"
version = start[9:-1]
simpler = {n.split("/", 1)[-1] for n in tar.getnames()[1:]}
@ -176,23 +176,23 @@ def test_build_global_dist(monkeypatch, tmpdir):
monkeypatch.chdir(MAIN_DIR)
monkeypatch.setenv("PYBIND11_GLOBAL_SDIST", "1")
out = subprocess.check_output(
[
sys.executable,
"setup.py",
"sdist",
"--formats=tar",
"--dist-dir",
"-m",
"build",
"--sdist",
"--outdir",
str(tmpdir),
]
)
if hasattr(out, "decode"):
out = out.decode()
(sdist,) = tmpdir.visit("*.tar")
(sdist,) = tmpdir.visit("*.tar.gz")
with tarfile.open(str(sdist)) as tar:
with tarfile.open(str(sdist), "r:gz") as tar:
start = tar.getnames()[0] + "/"
version = start[16:-1]
simpler = {n.split("/", 1)[-1] for n in tar.getnames()[1:]}

View File

@ -1,3 +1,4 @@
build==0.7.0
numpy==1.21.5; platform_python_implementation=="PyPy" and sys_platform=="linux" and python_version=="3.7"
numpy==1.19.3; platform_python_implementation!="PyPy" and python_version=="3.6"
numpy==1.21.5; platform_python_implementation!="PyPy" and python_version>="3.7" and python_version<"3.10"

View File

@ -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 <memory>
#include <stdexcept>
#include <utility>
@ -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_<Pickleable> pyPickleable(m, "Pickleable");
pyPickleable
.def(py::init<std::string>())
pyPickleable.def(py::init<std::string>())
.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<std::string>());
@ -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<std::string>());
p.setExtra1(t[1].cast<int>());
@ -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_<PickleableWithDict> pyPickleableWithDict(m, "PickleableWithDict", py::dynamic_attr());
py::class_<PickleableWithDict> pyPickleableWithDict(
m, "PickleableWithDict", py::dynamic_attr());
pyPickleableWithDict.def(py::init<std::string>())
.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<PickleableWithDict &>();
new (&p) PickleableWithDict(t[0].cast<std::string>());
@ -176,12 +172,13 @@ TEST_SUBMODULE(pickling, m) {
.def(py::init<std::string>())
.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<std::string>());
cpp_state.extra = t[1].cast<int>();