mirror of
https://github.com/pybind/pybind11.git
synced 2024-11-25 06:35:12 +00:00
feat: typing support for helpers (#2588)
* feat: basic typing support * docs: mention syncing as suggested by @rwgk * docs: update changelog * docs: copy of warning in limitations
This commit is contained in:
parent
a8c2e3eec5
commit
645d83813b
@ -34,8 +34,10 @@ repos:
|
|||||||
rev: 20.8b1
|
rev: 20.8b1
|
||||||
hooks:
|
hooks:
|
||||||
- id: black
|
- id: black
|
||||||
|
# By default, this ignores pyi files, though black supports them
|
||||||
|
types: [text]
|
||||||
# Not all Python files are Blacked, yet
|
# Not all Python files are Blacked, yet
|
||||||
files: ^(setup.py|pybind11|tests/extra)
|
files: ^(setup.py|pybind11|tests/extra|tools).*\.pyi?$
|
||||||
|
|
||||||
# Changes tabs to spaces
|
# Changes tabs to spaces
|
||||||
- repo: https://github.com/Lucas-C/pre-commit-hooks
|
- repo: https://github.com/Lucas-C/pre-commit-hooks
|
||||||
@ -60,6 +62,17 @@ repos:
|
|||||||
types: [file]
|
types: [file]
|
||||||
files: (\.cmake|CMakeLists.txt)(.in)?$
|
files: (\.cmake|CMakeLists.txt)(.in)?$
|
||||||
|
|
||||||
|
# Check static types with mypy
|
||||||
|
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||||
|
rev: v0.790
|
||||||
|
hooks:
|
||||||
|
- id: mypy
|
||||||
|
# The default Python type ignores .pyi files, so let's rerun if detected
|
||||||
|
types: [text]
|
||||||
|
files: ^pybind11.*\.pyi?$
|
||||||
|
# Running per-file misbehaves a bit, so just run on all files, it's fast
|
||||||
|
pass_filenames: false
|
||||||
|
|
||||||
# Checks the manifest for missing files (native support)
|
# Checks the manifest for missing files (native support)
|
||||||
- repo: https://github.com/mgedmin/check-manifest
|
- repo: https://github.com/mgedmin/check-manifest
|
||||||
rev: "0.43"
|
rev: "0.43"
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
recursive-include pybind11/include/pybind11 *.h
|
recursive-include pybind11/include/pybind11 *.h
|
||||||
recursive-include pybind11 *.py
|
recursive-include pybind11 *.py
|
||||||
|
recursive-include pybind11 py.typed
|
||||||
|
recursive-include pybind11 *.pyi
|
||||||
include pybind11/share/cmake/pybind11/*.cmake
|
include pybind11/share/cmake/pybind11/*.cmake
|
||||||
include LICENSE README.rst pyproject.toml setup.py setup.cfg
|
include LICENSE README.rst pyproject.toml setup.py setup.cfg
|
||||||
|
@ -72,7 +72,6 @@ API changes:
|
|||||||
* Public constructors for ``py::module_`` have been deprecated; please use
|
* Public constructors for ``py::module_`` have been deprecated; please use
|
||||||
``pybind11::module_::create_extension_module`` if you were using the public
|
``pybind11::module_::create_extension_module`` if you were using the public
|
||||||
constructor (fairly rare after ``PYBIND11_MODULE`` was introduced).
|
constructor (fairly rare after ``PYBIND11_MODULE`` was introduced).
|
||||||
**Provisional in 2.6.0rc1.**
|
|
||||||
`#2552 <https://github.com/pybind/pybind11/pull/2552>`_
|
`#2552 <https://github.com/pybind/pybind11/pull/2552>`_
|
||||||
|
|
||||||
* ``PYBIND11_OVERLOAD*`` macros and ``get_overload`` function replaced by
|
* ``PYBIND11_OVERLOAD*`` macros and ``get_overload`` function replaced by
|
||||||
@ -102,6 +101,9 @@ Packaging / building improvements:
|
|||||||
* ``pybind11-config`` is another way to write ``python -m pybind11`` if you
|
* ``pybind11-config`` is another way to write ``python -m pybind11`` if you
|
||||||
have your PATH set up.
|
have your PATH set up.
|
||||||
|
|
||||||
|
* Added external typing support to the helper module, code from
|
||||||
|
``import pybind11`` can now be type checked.
|
||||||
|
`#2588 <https://github.com/pybind/pybind11/pull/2588>`_
|
||||||
|
|
||||||
* Minimum CMake required increased to 3.4.
|
* Minimum CMake required increased to 3.4.
|
||||||
`#2338 <https://github.com/pybind/pybind11/pull/2338>`_ and
|
`#2338 <https://github.com/pybind/pybind11/pull/2338>`_ and
|
||||||
|
@ -51,3 +51,20 @@ clean, well written patch would likely be accepted to solve them.
|
|||||||
- The ``cpptest`` does not run on Windows with Python 3.8 or newer, due to DLL
|
- The ``cpptest`` does not run on Windows with Python 3.8 or newer, due to DLL
|
||||||
loader changes. User code that is correctly installed should not be affected.
|
loader changes. User code that is correctly installed should not be affected.
|
||||||
`#2560 <https://github.com/pybind/pybind11/issue/2560>`_
|
`#2560 <https://github.com/pybind/pybind11/issue/2560>`_
|
||||||
|
|
||||||
|
Python 3.9.0 warning
|
||||||
|
^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Combining older versions of pybind11 (< 2.6.0) with Python on 3.9.0 will
|
||||||
|
trigger undefined behavior that typically manifests as crashes during
|
||||||
|
interpreter shutdown (but could also destroy your data. **You have been
|
||||||
|
warned**).
|
||||||
|
|
||||||
|
This issue has been
|
||||||
|
`fixed in Python <https://github.com/python/cpython/pull/22670>`_. As a
|
||||||
|
mitigation until 3.9.1 is released and commonly used, pybind11 (2.6.0 or newer)
|
||||||
|
includes a temporary workaround specifically when Python 3.9.0 is detected at
|
||||||
|
runtime, leaking about 50 bytes of memory when a callback function is garbage
|
||||||
|
collected. For reference; the pybind11 test suite has about 2,000 such
|
||||||
|
callbacks, but only 49 are garbage collected before the end-of-process. Wheels
|
||||||
|
built with Python 3.9.0 will correctly avoid the leak when run in Python 3.9.1.
|
||||||
|
@ -25,7 +25,6 @@ C++ language rules change again.
|
|||||||
|
|
||||||
The public constructors of ``py::module_`` have been deprecated. Use
|
The public constructors of ``py::module_`` have been deprecated. Use
|
||||||
``PYBIND11_MODULE`` or ``module_::create_extension_module`` instead.
|
``PYBIND11_MODULE`` or ``module_::create_extension_module`` instead.
|
||||||
**Provisional in 2.6.0rc1.**
|
|
||||||
|
|
||||||
An error is now thrown when ``__init__`` is forgotten on subclasses. This was
|
An error is now thrown when ``__init__`` is forgotten on subclasses. This was
|
||||||
incorrect before, but was not checked. Add a call to ``__init__`` if it is
|
incorrect before, but was not checked. Add a call to ``__init__`` if it is
|
||||||
|
@ -9,6 +9,7 @@ from .commands import get_include, get_cmake_dir
|
|||||||
|
|
||||||
|
|
||||||
def print_includes():
|
def print_includes():
|
||||||
|
# type: () -> None
|
||||||
dirs = [
|
dirs = [
|
||||||
sysconfig.get_path("include"),
|
sysconfig.get_path("include"),
|
||||||
sysconfig.get_path("platinclude"),
|
sysconfig.get_path("platinclude"),
|
||||||
@ -18,13 +19,15 @@ def print_includes():
|
|||||||
# Make unique but preserve order
|
# Make unique but preserve order
|
||||||
unique_dirs = []
|
unique_dirs = []
|
||||||
for d in dirs:
|
for d in dirs:
|
||||||
if d not in unique_dirs:
|
if d and d not in unique_dirs:
|
||||||
unique_dirs.append(d)
|
unique_dirs.append(d)
|
||||||
|
|
||||||
print(" ".join("-I" + d for d in unique_dirs))
|
print(" ".join("-I" + d for d in unique_dirs))
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
# type: () -> None
|
||||||
|
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--includes",
|
"--includes",
|
||||||
|
6
pybind11/_version.pyi
Normal file
6
pybind11/_version.pyi
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
from typing import Union, Tuple
|
||||||
|
|
||||||
|
def _to_int(s: str) -> Union[int, str]: ...
|
||||||
|
|
||||||
|
__version__: str
|
||||||
|
version_info: Tuple[Union[int, str], ...]
|
@ -6,12 +6,14 @@ DIR = os.path.abspath(os.path.dirname(__file__))
|
|||||||
|
|
||||||
|
|
||||||
def get_include(user=False):
|
def get_include(user=False):
|
||||||
|
# type: (bool) -> str
|
||||||
installed_path = os.path.join(DIR, "include")
|
installed_path = os.path.join(DIR, "include")
|
||||||
source_path = os.path.join(os.path.dirname(DIR), "include")
|
source_path = os.path.join(os.path.dirname(DIR), "include")
|
||||||
return installed_path if os.path.exists(installed_path) else source_path
|
return installed_path if os.path.exists(installed_path) else source_path
|
||||||
|
|
||||||
|
|
||||||
def get_cmake_dir():
|
def get_cmake_dir():
|
||||||
|
# type: () -> str
|
||||||
cmake_installed_path = os.path.join(DIR, "share", "cmake", "pybind11")
|
cmake_installed_path = os.path.join(DIR, "share", "cmake", "pybind11")
|
||||||
if os.path.exists(cmake_installed_path):
|
if os.path.exists(cmake_installed_path):
|
||||||
return cmake_installed_path
|
return cmake_installed_path
|
||||||
|
0
pybind11/py.typed
Normal file
0
pybind11/py.typed
Normal file
@ -33,6 +33,12 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# IMPORTANT: If you change this file in the pybind11 repo, also review
|
||||||
|
# setup_helpers.pyi for matching changes.
|
||||||
|
#
|
||||||
|
# If you copy this file in, you don't
|
||||||
|
# need the .pyi file; it's just an interface file for static type checkers.
|
||||||
|
|
||||||
import contextlib
|
import contextlib
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
|
50
pybind11/setup_helpers.pyi
Normal file
50
pybind11/setup_helpers.pyi
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
# IMPORTANT: Should stay in sync with setup_helpers.py (mostly checked by CI /
|
||||||
|
# pre-commit).
|
||||||
|
|
||||||
|
from typing import Any, Iterator, Optional, Type, TypeVar, Union
|
||||||
|
from types import TracebackType
|
||||||
|
|
||||||
|
from distutils.command.build_ext import build_ext as _build_ext # type: ignore
|
||||||
|
from distutils.extension import Extension as _Extension
|
||||||
|
import distutils.ccompiler
|
||||||
|
import contextlib
|
||||||
|
|
||||||
|
WIN: bool
|
||||||
|
PY2: bool
|
||||||
|
MACOS: bool
|
||||||
|
STD_TMPL: str
|
||||||
|
|
||||||
|
class Pybind11Extension(_Extension):
|
||||||
|
def _add_cflags(self, *flags: str) -> None: ...
|
||||||
|
def _add_lflags(self, *flags: str) -> None: ...
|
||||||
|
def __init__(
|
||||||
|
self, *args: Any, cxx_std: int = 0, language: str = "c++", **kwargs: Any
|
||||||
|
) -> None: ...
|
||||||
|
@property
|
||||||
|
def cxx_std(self) -> int: ...
|
||||||
|
@cxx_std.setter
|
||||||
|
def cxx_std(self, level: int) -> None: ...
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def tmp_chdir() -> Iterator[str]: ...
|
||||||
|
def has_flag(compiler: distutils.ccompiler.CCompiler, flag: str) -> bool: ...
|
||||||
|
def auto_cpp_level(compiler: distutils.ccompiler.CCompiler) -> Union[int, str]: ...
|
||||||
|
|
||||||
|
class build_ext(_build_ext): # type: ignore
|
||||||
|
def build_extensions(self) -> None: ...
|
||||||
|
|
||||||
|
T = TypeVar("T", bound="ParallelCompile")
|
||||||
|
|
||||||
|
class ParallelCompile:
|
||||||
|
def __init__(
|
||||||
|
self, envvar: Optional[str] = None, default: int = 0, max: int = 0
|
||||||
|
): ...
|
||||||
|
def function(self) -> Any: ...
|
||||||
|
def install(self: T) -> T: ...
|
||||||
|
def __enter__(self: T) -> T: ...
|
||||||
|
def __exit__(
|
||||||
|
self,
|
||||||
|
exc_type: Optional[Type[BaseException]],
|
||||||
|
exc_value: Optional[BaseException],
|
||||||
|
traceback: Optional[TracebackType],
|
||||||
|
) -> None: ...
|
@ -64,3 +64,7 @@ ignore =
|
|||||||
N813
|
N813
|
||||||
# Black conflict
|
# Black conflict
|
||||||
W503, E203
|
W503, E203
|
||||||
|
|
||||||
|
[mypy]
|
||||||
|
files = pybind11
|
||||||
|
strict = True
|
||||||
|
@ -58,8 +58,11 @@ py_files = {
|
|||||||
"__init__.py",
|
"__init__.py",
|
||||||
"__main__.py",
|
"__main__.py",
|
||||||
"_version.py",
|
"_version.py",
|
||||||
|
"_version.pyi",
|
||||||
"commands.py",
|
"commands.py",
|
||||||
|
"py.typed",
|
||||||
"setup_helpers.py",
|
"setup_helpers.py",
|
||||||
|
"setup_helpers.pyi",
|
||||||
}
|
}
|
||||||
|
|
||||||
headers = main_headers | detail_headers
|
headers = main_headers | detail_headers
|
||||||
|
@ -19,7 +19,7 @@ if not os.path.exists(lib):
|
|||||||
|
|
||||||
libsize = os.path.getsize(lib)
|
libsize = os.path.getsize(lib)
|
||||||
|
|
||||||
print("------", os.path.basename(lib), "file size:", libsize, end='')
|
print("------", os.path.basename(lib), "file size:", libsize, end="")
|
||||||
|
|
||||||
if os.path.exists(save):
|
if os.path.exists(save):
|
||||||
with open(save) as sf:
|
with open(save) as sf:
|
||||||
@ -34,5 +34,5 @@ if os.path.exists(save):
|
|||||||
else:
|
else:
|
||||||
print()
|
print()
|
||||||
|
|
||||||
with open(save, 'w') as sf:
|
with open(save, "w") as sf:
|
||||||
sf.write(str(libsize))
|
sf.write(str(libsize))
|
||||||
|
@ -19,6 +19,7 @@ setup(
|
|||||||
"pybind11.share.cmake.pybind11",
|
"pybind11.share.cmake.pybind11",
|
||||||
],
|
],
|
||||||
package_data={
|
package_data={
|
||||||
|
"pybind11": ["py.typed", "*.pyi"],
|
||||||
"pybind11.include.pybind11": ["*.h"],
|
"pybind11.include.pybind11": ["*.h"],
|
||||||
"pybind11.include.pybind11.detail": ["*.h"],
|
"pybind11.include.pybind11.detail": ["*.h"],
|
||||||
"pybind11.share.cmake.pybind11": ["*.cmake"],
|
"pybind11.share.cmake.pybind11": ["*.cmake"],
|
||||||
|
Loading…
Reference in New Issue
Block a user