8000 Python typing stub generation by ddacw · Pull Request #20370 · opencv/opencv · GitHub
[go: up one dir, main page]

Skip to content

Python typing stub generation #20370

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
May 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions cmake/OpenCVDetectPython.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -297,3 +297,10 @@ elseif(PYTHON3_EXECUTABLE AND PYTHON3INTERP_FOUND)
set(PYTHON_DEFAULT_AVAILABLE "TRUE")
set(PYTHON_DEFAULT_EXECUTABLE "${PYTHON3_EXECUTABLE}")
endif()

if(PYTHON_DEFAULT_AVAILABLE)
execute_process(COMMAND ${PYTHON_DEFAULT_EXECUTABLE} --version
OUTPUT_VARIABLE PYTHON_DEFAULT_VERSION
OUTPUT_STRIP_TRAILING_WHITESPACE)
string(REGEX MATCH "[0-9]+.[0-9]+.[0-9]+" PYTHON_DEFAULT_VERSION "${PYTHON_DEFAULT_VERSION}")
endif()
7 changes: 7 additions & 0 deletions modules/python/bindings/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -76,18 +76,25 @@ set(cv2_generat 8000 ed_files

string(REPLACE ";" "\n" opencv_hdrs_ "${opencv_hdrs}")
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/headers.txt" "${opencv_hdrs_}")
file(GLOB_RECURSE typing_stubs_generation_files "${PYTHON_SOURCE_DIR}/src2/typing_stubs_generation/*.py")
add_custom_command(
OUTPUT ${cv2_generated_files}
COMMAND "${PYTHON_DEFAULT_EXECUTABLE}" "${PYTHON_SOURCE_DIR}/src2/gen2.py" "${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/headers.txt"
DEPENDS "${PYTHON_SOURCE_DIR}/src2/gen2.py"
"${PYTHON_SOURCE_DIR}/src2/hdr_parser.py"
"${typing_stubs_generation_files}"
"${PYTHON_SOURCE_DIR}/src2/typing_stubs_generator.py"
# not a real build dependency (file(WRITE) result): ${CMAKE_CURRENT_BINARY_DIR}/headers.txt
${opencv_hdrs}
COMMENT "Generate files for Python bindings and documentation"
)

add_custom_target(gen_opencv_python_source DEPENDS ${cv2_generated_files})

if(TARGET copy_opencv_typing_stubs)
add_dependencies(copy_opencv_typing_stubs gen_opencv_python_source)
endif()

set(cv2_custom_hdr "${CMAKE_CURRENT_BINARY_DIR}/pyopencv_custom_headers.h")
set(cv2_custom_hdr_str "//user-defined headers\n")
foreach(uh ${opencv_userdef_hdrs})
Expand Down
5 changes: 5 additions & 0 deletions modules/python/common.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ if(TARGET gen_opencv_python_source)
add_dependencies(${the_module} gen_opencv_python_source)
endif()

if(TARGET copy_opencv_typing_stubs)
# Python 3.6+
add_dependencies(${the_module} copy_opencv_typing_stubs)
endif()

ocv_assert(${PYTHON}_VERSION_MAJOR)
ocv_assert(${PYTHON}_VERSION_MINOR)

Expand Down
27 changes: 24 additions & 3 deletions modules/python/package/setup.py
8000
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
import os
import sys
import platform
import setuptools

SCRIPT_DIR=os.path.dirname(os.path.abspath(__file__))

SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))


def collect_module_typing_stub_files(root_module_path):
stub_files = []
for module_path, _, files in os.walk(root_module_path):
stub_files.extend(
map(lambda p: os.path.join(module_path, p),
filter(lambda f: f.endswith(".pyi"), files))
)
return stub_files


def main():
os.chdir(SCRIPT_DIR)
Expand All @@ -13,6 +23,13 @@ def main():

long_description = 'Open Source Computer Vision Library Python bindings' # TODO

root_module_path = os.path.join(SCRIPT_DIR, "cv2")
py_typed_path = os.path.join(root_module_path, "py.typed")
if os.path.isfile(py_typed_path):
typing_stub_files = collect_module_typing_stub_files(root_module_path)
if len(typing_stub_files) > 0:
typing_stub_files.append(py_typed_path)

setuptools.setup(
name=package_name,
version=package_version,
Expand All @@ -22,6 +39,9 @@ def main():
long_description=long_description,
long_description_content_type="text/markdown",
packages=setuptools.find_packages(),
package_data={
"cv2": typing_stub_files
},
maintainer="OpenCV Team",
install_requires="numpy",
classifiers=[
Expand Down Expand Up @@ -55,5 +75,6 @@ def main():
],
)


if __name__ == '__main__':
main()
19 changes: 17 additions & 2 deletions modules/python/python_loader.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,6 @@ foreach(fname ${PYTHON_LOADER_FILES})
endif()
endforeach()



if(WIN32)
if(CMAKE_GENERATOR MATCHES "Visual Studio")
list(APPEND CMAKE_PYTHON_BINARIES_PATH "'${EXECUTABLE_OUTPUT_PATH}/Release'") # TODO: CMAKE_BUILD_TYPE is not defined
Expand Down Expand Up @@ -126,3 +124,20 @@ if(NOT "${OPENCV_PYTHON_EXTRA_MODULES_PATH}" STREQUAL "")
ocv_add_python_files_from_path(${extra_ocv_py_modules_path})
endforeach()
endif()

if(${PYTHON}_VERSION_STRING VERSION_GREATER "3.6" AND PYTHON_DEFAULT_VERSION VERSION_GREATER "3.6")
add_custom_target(copy_opencv_typing_stubs)
# Copy all generated stub files to python_loader directory only if
# generation succeeds, this behvoir can't be achieved with default
# CMake constructions, because failed generation produces a warning instead of
# halts on hard error.
add_custom_command(
TARGET copy_opencv_typing_stubs
COMMAND ${PYTHON_DEFAULT_EXECUTABLE} ${PYTHON_SOURCE_DIR}/src2/copy_typings_stubs_on_success.py
--stubs_dir ${OPENCV_PYTHON_BINDINGS_DIR}/cv2
--output_dir ${__loader_path}/cv2
)
if(DEFINED OPENCV_PYTHON_INSTALL_PATH)
install(DIRECTORY "${OPENCV_PYTHON_BINDINGS_DIR}/cv2" DESTINATION "${OPENCV_PYTHON_INSTALL_PATH}" COMPONENT python)
endif()
endif()
45 changes: 45 additions & 0 deletions modules/python/src2/copy_typings_stubs_on_success.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import argparse
import warnings
import os
import sys

if sys.version_info >= (3, 8, ):
# shutil.copytree received the `dirs_exist_ok` parameter
from functools import partial
import shutil

copy_tree = partial(shutil.copytree, dirs_exist_ok=True)
else:
from distutils.dir_util import copy_tree


def main():
args = parse_arguments()
py_typed_path = os.path.join(args.stubs_dir, 'py.typed')
if not os.path.isfile(py_typed_path):
warnings.warn(
'{} is missing, it means that typings stubs generation is either '
'failed or has been skipped. Ensure that Python 3.6+ is used for '
'build and there is no warnings during Python source code '
'generation phase.'.format(py_typed_path)
)
return
copy_tree(args.stubs_dir, args.output_dir)


def parse_arguments():
parser = argparse.ArgumentParser(
description='Copies generated typing stubs only when generation '
'succeeded. This is identified by presence of the `py.typed` file '
'inside typing stubs directory.'
)
parser.add_argument('--stubs_dir', type=str,
help='Path to directory containing generated typing '
'stubs file')
parser.add_argument('--output_dir', type=str,
help='Path to output directory')
return parser.parse_args()


if __name__ == '__main__':
main()
Loading
0