Skip to content

Commit 6747bf1

Browse files
committed
[MLIR][Python] reland stubgen v2
1 parent 3c810b7 commit 6747bf1

File tree

11 files changed

+223
-2930
lines changed

11 files changed

+223
-2930
lines changed

.ci/all_requirements.txt

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -194,9 +194,9 @@ ml-dtypes==0.5.1 ; python_version < "3.13" \
194194
--hash=sha256:d13755f8e8445b3870114e5b6240facaa7cb0c3361e54beba3e07fa912a6e12b \
195195
--hash=sha256:fd918d4e6a4e0c110e2e05be7a7814d10dc1b95872accbf6512b80a109b71ae1
196196
# via -r mlir/python/requirements.txt
197-
nanobind==2.7.0 \
198-
--hash=sha256:73b12d0e751d140d6c1bf4b215e18818a8debfdb374f08dc3776ad208d808e74 \
199-
--hash=sha256:f9f1b160580c50dcf37b6495a0fd5ec61dc0d95dae5f8004f87dd9ad7eb46b34
197+
nanobind==2.9.2 \
198+
--hash=sha256:c37957ffd5eac7eda349cff3622ecd32e5ee1244ecc912c99b5bc8188bafd16e \
199+
--hash=sha256:e7608472de99d375759814cab3e2c94aba3f9ec80e62cfef8ced495ca5c27d6e
200200
# via -r mlir/python/requirements.txt
201201
numpy==2.0.2 \
202202
--hash=sha256:0123ffdaa88fa4ab64835dcbde75dcdf89c453c922f18dced6e27c90d1d0ec5a \
@@ -383,6 +383,10 @@ swig==4.3.1 \
383383
--hash=sha256:efec16327029f682f649a26da726bb0305be8800bd0f1fa3e81bf0769cf5b476 \
384384
--hash=sha256:fc496c0d600cf1bb2d91e28d3d6eae9c4301e5ea7a0dec5a4281b5efed4245a8
385385
# via -r lldb/test/requirements.txt
386+
typing-extensions==4.15.0 \
387+
--hash=sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466 \
388+
--hash=sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548
389+
# via -r mlir/python/requirements.txt
386390
urllib3==2.5.0 \
387391
--hash=sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760 \
388392
--hash=sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc

mlir/cmake/modules/AddMLIRPython.cmake

Lines changed: 98 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,84 @@ function(declare_mlir_python_sources name)
9999
endif()
100100
endfunction()
101101

102+
# Function: generate_type_stubs
103+
# Turns on automatic type stub generation for extension modules.
104+
# Specifically, performs add_custom_command to run nanobind's stubgen on an extension module.
105+
#
106+
# Arguments:
107+
# MODULE_NAME: The fully-qualified name of the extension module (used for importing in python).
108+
# DEPENDS_TARGETS: List of targets these type stubs depend on being built; usually corresponding to the
109+
# specific extension module (e.g., something like StandalonePythonModules.extension._standaloneDialectsNanobind.dso)
110+
# and the core bindings extension module (e.g., something like StandalonePythonModules.extension._mlir.dso).
111+
# OUTPUT_DIR: The root output directory to emit the type stubs into.
112+
# OUTPUTS: List of expected outputs.
113+
# DEPENDS_TARGET_SRC_DEPS: List of cpp sources for extension library (for generating a DEPFILE).
114+
# IMPORT_PATHS: List of paths to add to PYTHONPATH for stubgen.
115+
# Outputs:
116+
# NB_STUBGEN_CUSTOM_TARGET: The target corresponding to generation which other targets can depend on.
117+
function(generate_type_stubs)
118+
cmake_parse_arguments(ARG
119+
""
120+
"MODULE_NAME;OUTPUT_DIR"
121+
"IMPORT_PATHS;DEPENDS_TARGETS;OUTPUTS;DEPENDS_TARGET_SRC_DEPS"
122+
${ARGN})
123+
124+
# for people doing find_package(nanobind)
125+
if(EXISTS ${nanobind_DIR}/../src/stubgen.py)
126+
set(NB_STUBGEN "${nanobind_DIR}/../src/stubgen.py")
127+
elseif(EXISTS ${nanobind_DIR}/../stubgen.py)
128+
set(NB_STUBGEN "${nanobind_DIR}/../stubgen.py")
129+
# for people using FetchContent_Declare and FetchContent_MakeAvailable
130+
elseif(EXISTS ${nanobind_SOURCE_DIR}/src/stubgen.py)
131+
set(NB_STUBGEN "${nanobind_SOURCE_DIR}/src/stubgen.py")
132+
elseif(EXISTS ${nanobind_SOURCE_DIR}/stubgen.py)
133+
set(NB_STUBGEN "${nanobind_SOURCE_DIR}/stubgen.py")
134+
else()
135+
message(FATAL_ERROR "generate_type_stubs(): could not locate 'stubgen.py'!")
136+
endif()
137+
138+
file(REAL_PATH "${NB_STUBGEN}" NB_STUBGEN)
139+
set(_import_paths)
140+
foreach(_import_path IN LISTS ARG_IMPORT_PATHS)
141+
file(REAL_PATH "${_import_path}" _import_path)
142+
list(APPEND _import_paths "-i;${_import_path}")
143+
endforeach()
144+
set(_nb_stubgen_cmd
145+
"${Python_EXECUTABLE}"
146+
"${NB_STUBGEN}"
147+
--module
148+
"${ARG_MODULE_NAME}"
149+
"${_import_paths}"
150+
--recursive
151+
--include-private
152+
--output-dir
153+
"${ARG_OUTPUT_DIR}")
154+
155+
list(TRANSFORM ARG_OUTPUTS PREPEND "${ARG_OUTPUT_DIR}/" OUTPUT_VARIABLE _generated_type_stubs)
156+
set(_depfile "${ARG_OUTPUT_DIR}/${ARG_MODULE_NAME}.d")
157+
if ((NOT EXISTS ${_depfile}) AND ARG_DEPENDS_TARGET_SRC_DEPS)
158+
list(JOIN ARG_DEPENDS_TARGET_SRC_DEPS " " _depfiles)
159+
list(TRANSFORM _generated_type_stubs APPEND ": ${_depfiles}" OUTPUT_VARIABLE _depfiles)
160+
list(JOIN _depfiles "\n" _depfiles)
161+
file(GENERATE OUTPUT "${_depfile}" CONTENT "${_depfiles}")
162+
endif()
163+
164+
message(DEBUG "Generating type-stubs outputs ${_generated_type_stubs}")
165+
add_custom_command(
166+
OUTPUT ${_generated_type_stubs}
167+
COMMAND ${_nb_stubgen_cmd}
168+
WORKING_DIRECTORY "${CMAKE_CURRENT_FUNCTION_LIST_DIR}"
169+
DEPENDS "${ARG_DEPENDS_TARGETS}"
170+
DEPFILE "${_depfile}"
171+
COMMENT "Generating type stubs using: ${_nb_stubgen_cmd}"
172+
)
173+
174+
list(JOIN ARG_DEPENDS_TARGETS "." _name)
175+
set(_name "${_name}.${ARG_MODULE_NAME}.type_stubs")
176+
add_custom_target("${_name}" DEPENDS ${_generated_type_stubs})
177+
set(NB_STUBGEN_CUSTOM_TARGET "${_name}" PARENT_SCOPE)
178+
endfunction()
179+
102180
# Function: declare_mlir_python_extension
103181
# Declares a buildable python extension from C++ source files. The built
104182
# module is considered a python source file and included as everything else.
@@ -678,26 +756,28 @@ function(add_mlir_python_extension libname extname)
678756
# the super project handle compile options as it wishes.
679757
get_property(NB_LIBRARY_TARGET_NAME TARGET ${libname} PROPERTY LINK_LIBRARIES)
680758
target_compile_options(${NB_LIBRARY_TARGET_NAME}
681-
PRIVATE
682-
-Wall -Wextra -Wpedantic
683-
-Wno-c++98-compat-extra-semi
684-
-Wno-cast-qual
685-
-Wno-covered-switch-default
686-
-Wno-nested-anon-types
687-
-Wno-unused-parameter
688-
-Wno-zero-length-array
689-
${eh_rtti_enable})
759+
PRIVATE
760+
-Wall -Wextra -Wpedantic
761+
-Wno-c++98-compat-extra-semi
762+
-Wno-cast-qual
763+
-Wno-covered-switch-default
764+
-Wno-deprecated-literal-operator
765+
-Wno-nested-anon-types
766+
-Wno-unused-parameter
767+
-Wno-zero-length-array
768+
${eh_rtti_enable})
690769

691770
target_compile_options(${libname}
692-
PRIVATE
693-
-Wall -Wextra -Wpedantic
694-
-Wno-c++98-compat-extra-semi
695-
-Wno-cast-qual
696-
-Wno-covered-switch-default
697-
-Wno-nested-anon-types
698-
-Wno-unused-parameter
699-
-Wno-zero-length-array
700-
${eh_rtti_enable})
771+
PRIVATE
772+
-Wall -Wextra -Wpedantic
773+
-Wno-c++98-compat-extra-semi
774+
-Wno-cast-qual
775+
-Wno-covered-switch-default
776+
-Wno-deprecated-literal-operator
777+
-Wno-nested-anon-types
778+
-Wno-unused-parameter
779+
-Wno-zero-length-array
780+
${eh_rtti_enable})
701781
endif()
702782

703783
if(APPLE)

mlir/cmake/modules/MLIRDetectPythonEnv.cmake

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ macro(mlir_configure_python_dev_packages)
5454
"extension = '${PYTHON_MODULE_EXTENSION}")
5555

5656
mlir_detect_nanobind_install()
57-
find_package(nanobind 2.4 CONFIG REQUIRED)
57+
find_package(nanobind 2.9 CONFIG REQUIRED)
5858
message(STATUS "Found nanobind v${nanobind_VERSION}: ${nanobind_INCLUDE_DIR}")
5959
message(STATUS "Python prefix = '${PYTHON_MODULE_PREFIX}', "
6060
"suffix = '${PYTHON_MODULE_SUFFIX}', "

mlir/examples/standalone/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ if(MLIR_ENABLE_BINDINGS_PYTHON)
5555
include(MLIRDetectPythonEnv)
5656
mlir_configure_python_dev_packages()
5757
set(MLIR_PYTHON_PACKAGE_PREFIX "mlir_standalone" CACHE STRING "" FORCE)
58-
set(MLIR_BINDINGS_PYTHON_INSTALL_PREFIX "python_packages/standalone/mlir_standalone" CACHE STRING "" FORCE)
58+
set(MLIR_BINDINGS_PYTHON_INSTALL_PREFIX "python_packages/standalone/${MLIR_PYTHON_PACKAGE_PREFIX}" CACHE STRING "" FORCE)
5959
add_subdirectory(python)
6060
endif()
6161
add_subdirectory(test)

mlir/examples/standalone/python/CMakeLists.txt

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,9 @@ add_mlir_python_common_capi_library(StandalonePythonCAPI
6464
# Instantiation of all Python modules
6565
################################################################################
6666

67+
set(StandalonePythonModules_ROOT_PREFIX "${MLIR_BINARY_DIR}/${MLIR_BINDINGS_PYTHON_INSTALL_PREFIX}")
6768
add_mlir_python_modules(StandalonePythonModules
68-
ROOT_PREFIX "${MLIR_BINARY_DIR}/${MLIR_BINDINGS_PYTHON_INSTALL_PREFIX}"
69+
ROOT_PREFIX "${StandalonePythonModules_ROOT_PREFIX}"
6970
INSTALL_PREFIX "${MLIR_BINDINGS_PYTHON_INSTALL_PREFIX}"
7071
DECLARED_SOURCES
7172
StandalonePythonSources
@@ -76,4 +77,30 @@ add_mlir_python_modules(StandalonePythonModules
7677
MLIRPythonSources.Dialects.builtin
7778
COMMON_CAPI_LINK_LIBS
7879
StandalonePythonCAPI
79-
)
80+
)
81+
82+
# Everything here is very tightly coupled. See the ample descriptions at the bottom of
83+
# mlir/python/CMakeLists.txt.
84+
get_target_property(_standalone_extension_srcs StandalonePythonSources.NanobindExtension INTERFACE_SOURCES)
85+
generate_type_stubs(
86+
MODULE_NAME mlir_standalone._mlir_libs._standaloneDialectsNanobind
87+
DEPENDS_TARGETS
88+
StandalonePythonModules.extension._mlir.dso
89+
StandalonePythonModules.extension._standaloneDialectsNanobind.dso
90+
OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/type_stubs/_mlir_libs"
91+
OUTPUTS
92+
_standaloneDialectsNanobind/__init__.pyi
93+
_standaloneDialectsNanobind/standalone.pyi
94+
DEPENDS_TARGET_SRC_DEPS "${_standalone_extension_srcs}"
95+
IMPORT_PATHS "${StandalonePythonModules_ROOT_PREFIX}/.."
96+
)
97+
add_dependencies(StandalonePythonModules "${NB_STUBGEN_CUSTOM_TARGET}")
98+
99+
declare_mlir_python_sources(
100+
StandalonePythonSources.type_stub_gen
101+
ROOT_DIR "${CMAKE_CURRENT_BINARY_DIR}/type_stubs"
102+
ADD_TO_PARENT StandalonePythonSources
103+
SOURCES
104+
_mlir_libs/_standaloneDialectsNanobind/__init__.pyi
105+
_mlir_libs/_standaloneDialectsNanobind/standalone.pyi
106+
)

mlir/python/CMakeLists.txt

Lines changed: 83 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
include(AddMLIRPython)
22

3+
# Specifies that all MLIR packages are co-located under the `mlir_standalone`
4+
# top level package (the API has been embedded in a relocatable way).
5+
add_compile_definitions("MLIR_PYTHON_PACKAGE_PREFIX=${MLIR_PYTHON_PACKAGE_PREFIX}.")
6+
37
################################################################################
48
# Structural groupings.
59
################################################################################
@@ -23,11 +27,6 @@ declare_mlir_python_sources(MLIRPythonSources.Core.Python
2327
passmanager.py
2428
rewrite.py
2529
dialects/_ods_common.py
26-
27-
# The main _mlir module has submodules: include stubs from each.
28-
_mlir_libs/_mlir/__init__.pyi
29-
_mlir_libs/_mlir/ir.pyi
30-
_mlir_libs/_mlir/passmanager.pyi
3130
)
3231

3332
declare_mlir_python_sources(MLIRPythonSources.Core.Python.Extras
@@ -663,7 +662,7 @@ if(MLIR_ENABLE_EXECUTION_ENGINE)
663662
MODULE_NAME _mlirExecutionEngine
664663
ADD_TO_PARENT MLIRPythonSources.ExecutionEngine
665664
ROOT_DIR "${PYTHON_SOURCE_DIR}"
666-
PYTHON_BINDINGS_LIBRARY nanobind
665+
PYTHON_BINDINGS_LIBRARY nanobind
667666
SOURCES
668667
ExecutionEngineModule.cpp
669668
PRIVATE_LINK_LIBS
@@ -814,10 +813,11 @@ endif()
814813
# once ready.
815814
################################################################################
816815

816+
set(MLIRPythonModules_ROOT_PREFIX "${MLIR_BINARY_DIR}/${MLIR_BINDINGS_PYTHON_INSTALL_PREFIX}")
817817
add_mlir_python_common_capi_library(MLIRPythonCAPI
818818
INSTALL_COMPONENT MLIRPythonModules
819819
INSTALL_DESTINATION "${MLIR_BINDINGS_PYTHON_INSTALL_PREFIX}/_mlir_libs"
820-
OUTPUT_DIRECTORY "${MLIR_BINARY_DIR}/python_packages/mlir_core/mlir/_mlir_libs"
820+
OUTPUT_DIRECTORY "${MLIRPythonModules_ROOT_PREFIX}/_mlir_libs"
821821
RELATIVE_INSTALL_ROOT "../../../.."
822822
DECLARED_HEADERS
823823
MLIRPythonCAPI.HeaderSources
@@ -846,7 +846,7 @@ endif()
846846
################################################################################
847847

848848
add_mlir_python_modules(MLIRPythonModules
849-
ROOT_PREFIX "${MLIR_BINARY_DIR}/python_packages/mlir_core/mlir"
849+
ROOT_PREFIX ${MLIRPythonModules_ROOT_PREFIX}
850850
INSTALL_PREFIX "${MLIR_BINDINGS_PYTHON_INSTALL_PREFIX}"
851851
DECLARED_SOURCES
852852
MLIRPythonSources
@@ -855,3 +855,78 @@ add_mlir_python_modules(MLIRPythonModules
855855
COMMON_CAPI_LINK_LIBS
856856
MLIRPythonCAPI
857857
)
858+
859+
# _mlir stubgen
860+
861+
set(_core_type_stub_sources
862+
_mlir/__init__.pyi
863+
_mlir/ir.pyi
864+
_mlir/passmanager.pyi
865+
_mlir/rewrite.pyi
866+
)
867+
868+
# Note 1: INTERFACE_SOURCES is a genex ($<BUILD_INTERFACE> $<INSTALL_INTERFACE>)
869+
# which will be evaluated by file(GENERATE ...) inside generate_type_stubs. This will evaluate to the correct
870+
# thing in the build dir (i.e., actual source dir paths) and in the install dir (where it's a conventional path; see install/lib/cmake/mlir/MLIRTargets.cmake).
871+
#
872+
# Note 2: MLIRPythonExtension.Core is the target that is defined using target_sources(INTERFACE) **NOT** MLIRPythonModules.extension._mlir.dso.
873+
# So be sure to use the correct target!
874+
get_target_property(_core_extension_srcs MLIRPythonExtension.Core INTERFACE_SOURCES)
875+
876+
# Why is MODULE_NAME _mlir_libs._mlir here but mlir._mlir_libs._mlirPythonTestNanobind below???
877+
# The _mlir extension can be imported independently of any other python code and/or extension modules.
878+
# I.e., you could do `cd $MLIRPythonModules_ROOT_PREFIX && python -c "import _mlir_libs._mlir"` (try it!).
879+
# _mlir is also (currently) the only extension for which this is possible because dialect extensions modules,
880+
# which generally make use of `mlir_value_subclass/mlir_type_subclass/mlir_attribute_subclass`, perform an
881+
# `import mlir` right when they're loaded (see the mlir_*_subclass ctors in NanobindAdaptors.h).
882+
# Note, this also why IMPORT_PATHS "${MLIRPythonModules_ROOT_PREFIX}" here while below
883+
# "${MLIRPythonModules_ROOT_PREFIX}/.." (because MLIR_BINDINGS_PYTHON_INSTALL_PREFIX, by default, ends at mlir).
884+
#
885+
# Further note: this function creates file targets like "${CMAKE_CURRENT_BINARY_DIR}/type_stubs/_mlir_libs/_mlir/__init__.pyi".
886+
# These must match the file targets that declare_mlir_python_sources expects, which are like "${ROOT_DIR}/${WHATEVER_SOURCE}".
887+
# This is why _mlir_libs is prepended below.
888+
generate_type_stubs(
889+
MODULE_NAME _mlir_libs._mlir
890+
DEPENDS_TARGETS MLIRPythonModules.extension._mlir.dso
891+
OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/type_stubs/_mlir_libs"
892+
OUTPUTS "${_core_type_stub_sources}"
893+
DEPENDS_TARGET_SRC_DEPS "${_core_extension_srcs}"
894+
IMPORT_PATHS "${MLIRPythonModules_ROOT_PREFIX}"
895+
)
896+
add_dependencies(MLIRPythonModules "${NB_STUBGEN_CUSTOM_TARGET}")
897+
898+
list(TRANSFORM _core_type_stub_sources PREPEND "_mlir_libs/")
899+
declare_mlir_python_sources(
900+
MLIRPythonExtension.Core.type_stub_gen
901+
ROOT_DIR "${CMAKE_CURRENT_BINARY_DIR}/type_stubs"
902+
ADD_TO_PARENT MLIRPythonSources.Core
903+
SOURCES "${_core_type_stub_sources}"
904+
)
905+
906+
# _mlirPythonTestNanobind stubgen
907+
908+
if(MLIR_INCLUDE_TESTS)
909+
get_target_property(_test_extension_srcs MLIRPythonTestSources.PythonTestExtensionNanobind INTERFACE_SOURCES)
910+
generate_type_stubs(
911+
# This is the FQN path because dialect modules import _mlir when loaded. See above.
912+
MODULE_NAME mlir._mlir_libs._mlirPythonTestNanobind
913+
DEPENDS_TARGETS
914+
# You need both _mlir and _mlirPythonTestNanobind because dialect modules import _mlir when loaded
915+
# (so _mlir needs to be built before calling stubgen).
916+
MLIRPythonModules.extension._mlir.dso
917+
MLIRPythonModules.extension._mlirPythonTestNanobind.dso
918+
# You need this one so that ir.py "built" because mlir._mlir_libs.__init__.py import mlir.ir in _site_initialize.
919+
MLIRPythonModules.sources.MLIRPythonSources.Core.Python
920+
OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/type_stubs/_mlir_libs"
921+
OUTPUTS _mlirPythonTestNanobind.pyi
922+
DEPENDS_TARGET_SRC_DEPS "${_test_extension_srcs}"
923+
IMPORT_PATHS "${MLIRPythonModules_ROOT_PREFIX}/.."
924+
)
925+
add_dependencies(MLIRPythonModules "${NB_STUBGEN_CUSTOM_TARGET}")
926+
declare_mlir_python_sources(
927+
MLIRPythonTestSources.PythonTestExtensionNanobind.type_stub_gen
928+
ROOT_DIR "${CMAKE_CURRENT_BINARY_DIR}/type_stubs"
929+
ADD_TO_PARENT MLIRPythonTestSources.Dialects
930+
SOURCES _mlir_libs/_mlirPythonTestNanobind.pyi
931+
)
932+
endif()

mlir/python/mlir/_mlir_libs/_mlir/__init__.pyi

Lines changed: 0 additions & 12 deletions
This file was deleted.

0 commit comments

Comments
 (0)