diff --git a/CMakeLists.txt b/CMakeLists.txt
index e53f375e9a..f19cc10502 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -362,6 +362,9 @@ if(BUILD_PYTHON)
"NOT BUILD_DEPS" ON)
message(STATUS "Python: Build pybind11_protobuf: ${BUILD_pybind11_protobuf}")
+ option(GENERATE_PYTHON_STUB "Generate Python stub file (.pyi)" ON)
+ message(STATUS "Python: Generate stub file: ${GENERATE_PYTHON_STUB}")
+
CMAKE_DEPENDENT_OPTION(BUILD_PYTHON_DOC "Build the Python doc" OFF "NOT BUILD_DOC" ON)
message(STATUS "Python: Build doc: ${BUILD_PYTHON_DOC}")
diff --git a/bazel/notebook_requirements.in b/bazel/notebook_requirements.in
index 385bd5f2ee..6a31d07b0b 100644
--- a/bazel/notebook_requirements.in
+++ b/bazel/notebook_requirements.in
@@ -5,6 +5,7 @@ protobuf==4.24.4
scipy==1.11.1
# OR-Tools build dependencies
+mypy==1.6.1
mypy-protobuf==3.5.0
virtualenv==20.24.2
black==23.7.0
diff --git a/bazel/notebook_requirements.txt b/bazel/notebook_requirements.txt
index 96cb767393..7a55294808 100644
--- a/bazel/notebook_requirements.txt
+++ b/bazel/notebook_requirements.txt
@@ -158,8 +158,12 @@ matplotlib-inline==0.1.6
# ipython
mistune==3.0.1
# via nbconvert
+mypy==1.6.1
+ # via -r bazel/notebook_requirements.in
mypy-extensions==1.0.0
- # via black
+ # via
+ # black
+ # mypy
mypy-protobuf==3.5.0
# via -r bazel/notebook_requirements.in
nbclient==0.8.0
@@ -340,6 +344,8 @@ traitlets==5.9.0
# qtconsole
types-protobuf==4.24.0.0
# via mypy-protobuf
+typing-extensions==4.8.0
+ # via mypy
tzdata==2023.3
# via pandas
uri-template==1.3.0
diff --git a/bazel/ortools_requirements.in b/bazel/ortools_requirements.in
index 180b6149fd..5f114deb95 100644
--- a/bazel/ortools_requirements.in
+++ b/bazel/ortools_requirements.in
@@ -5,6 +5,7 @@ protobuf==4.24.4
scipy==1.11.1
# OR-Tools build dependencies
+mypy==1.6.1
mypy-protobuf==3.5.0
virtualenv==20.24.2
black==23.7.0
diff --git a/bazel/ortools_requirements.txt b/bazel/ortools_requirements.txt
index 96cd33620e..960827bfbc 100644
--- a/bazel/ortools_requirements.txt
+++ b/bazel/ortools_requirements.txt
@@ -14,8 +14,12 @@ distlib==0.3.7
# via virtualenv
filelock==3.12.2
# via virtualenv
+mypy==1.6.1
+ # via -r bazel/ortools_requirements.in
mypy-extensions==1.0.0
- # via black
+ # via
+ # black
+ # mypy
mypy-protobuf==3.5.0
# via -r bazel/ortools_requirements.in
numpy==1.25.2
@@ -47,6 +51,8 @@ six==1.16.0
# via python-dateutil
types-protobuf==4.24.0.0
# via mypy-protobuf
+typing-extensions==4.8.0
+ # via mypy
tzdata==2023.3
# via pandas
virtualenv==20.24.2
diff --git a/cmake/README.md b/cmake/README.md
index 43fb96ec70..76c7dcca4c 100644
--- a/cmake/README.md
+++ b/cmake/README.md
@@ -253,6 +253,8 @@ cmake -S. -Bbuild -LH
| `BUILD_FAT_JAR` | OFF | Build a `ortools-java` .jar that includes all of its own Maven dependencies, including the native package
Only available if `BUILD_JAVA=ON` |
| | | |
| `BUILD_pybind11` | `BUILD_DEPS` | Static build the pybind11 libraries
**Forced** to ON if `BUILD_DEPS=ON`
Only available if `BUILD_PYTHON=ON` |
+| `BUILD_pybind11_protobuf` | `BUILD_DEPS` | Static build the pybind11_protobuf libraries
**Forced** to ON if `BUILD_DEPS=ON`
Only available if `BUILD_PYTHON=ON` |
+| `GENERATE_PYTHON_STUB` | ON | Generate python stub files
Only available if `BUILD_PYTHON=ON` |
| `BUILD_VENV` | `BUILD_TESTING` | Create python venv in `BINARY_DIR/python/venv`
**Forced** to ON if `BUILD_TESTING=ON`
Only available if `BUILD_PYTHON=ON` |
| `VENV_USE_SYSTEM_SITE_PACKAGES` | OFF | Python venv can use system site package (e.g. `py3-numpy` on Alpine)
Only available if `BUILD_PYTHON=ON` and `BUILD_VENV=ON` |
| `FETCH_PYTHON_DEPS` | `BUILD_DEPS` | Fetch python modules needed to build ortools package
Only available if `BUILD_PYTHON=ON` |
diff --git a/cmake/python.cmake b/cmake/python.cmake
index ed28516d53..6175596187 100644
--- a/cmake/python.cmake
+++ b/cmake/python.cmake
@@ -308,6 +308,56 @@ configure_file(
${PROJECT_BINARY_DIR}/python/README.txt
COPYONLY)
+# Generate Stub
+if(GENERATE_PYTHON_STUB)
+# Look for required python modules
+search_python_module(
+ NAME mypy
+ PACKAGE mypy
+ NO_VERSION)
+
+find_program(
+ stubgen_EXECUTABLE
+ NAMES stubgen stubgen.exe
+ REQUIRED
+)
+
+add_custom_command(
+ OUTPUT python/stub/timestamp
+ COMMAND ${CMAKE_COMMAND} -E remove_directory stub
+ COMMAND ${CMAKE_COMMAND} -E make_directory stub
+ COMMAND ${stubgen_EXECUTABLE} -p ortools.init.python.init --output .
+ COMMAND ${stubgen_EXECUTABLE} -p ortools.algorithms.python.knapsack_solver --output .
+ COMMAND ${stubgen_EXECUTABLE} -p ortools.graph.python.linear_sum_assignment --output .
+ COMMAND ${stubgen_EXECUTABLE} -p ortools.graph.python.max_flow --output .
+ COMMAND ${stubgen_EXECUTABLE} -p ortools.graph.python.min_cost_flow --output .
+ COMMAND ${stubgen_EXECUTABLE} -p ortools.constraint_solver.pywrapcp --output .
+ COMMAND ${stubgen_EXECUTABLE} -p ortools.linear_solver.pywraplp --output .
+ COMMAND ${stubgen_EXECUTABLE} -p ortools.linear_solver.python.model_builder_helper --output .
+ COMMAND ${stubgen_EXECUTABLE} -p ortools.pdlp.python.pdlp --output .
+ COMMAND ${stubgen_EXECUTABLE} -p ortools.sat.python.swig_helper --output .
+ COMMAND ${stubgen_EXECUTABLE} -p ortools.scheduling.python.rcpsp --output .
+ COMMAND ${stubgen_EXECUTABLE} -p ortools.util.python.sorted_interval_list --output .
+ COMMAND ${CMAKE_COMMAND} -E touch ${PROJECT_BINARY_DIR}/python/stub/timestamp
+ MAIN_DEPENDENCY
+ ortools/python/setup.py.in
+ DEPENDS
+ init_pybind11
+ knapsack_solver_pybind11
+ linear_sum_assignment_pybind11
+ max_flow_pybind11
+ min_cost_flow_pybind11
+ pywrapcp
+ pywraplp
+ model_builder_helper_pybind11
+ pdlp_pybind11
+ swig_helper_pybind11
+ rcpsp_pybind11
+ sorted_interval_list_pybind11
+ WORKING_DIRECTORY python
+ COMMAND_EXPAND_LISTS)
+endif()
+
# Look for required python modules
search_python_module(
NAME setuptools
@@ -357,6 +407,7 @@ add_custom_command(
swig_helper_pybind11
rcpsp_pybind11
sorted_interval_list_pybind11
+ $<$:python/stub/timestamp>
BYPRODUCTS
python/${PYTHON_PROJECT}
python/${PYTHON_PROJECT}.egg-info
diff --git a/ortools/python/setup.py.in b/ortools/python/setup.py.in
index 05a0b056eb..f0e21ec3fb 100644
--- a/ortools/python/setup.py.in
+++ b/ortools/python/setup.py.in
@@ -50,21 +50,21 @@ setup(
],
package_data={
'@PYTHON_PROJECT@':[$<$,SHARED_LIBRARY>:'.libs/*','../$'>],
- '@PYTHON_PROJECT@.init.python':['$'],
- '@PYTHON_PROJECT@.algorithms.python':['$'],
+ '@PYTHON_PROJECT@.init.python':['$', '*.pyi'],
+ '@PYTHON_PROJECT@.algorithms.python':['$', '*.pyi'],
'@PYTHON_PROJECT@.bop':['*.pyi'],
'@PYTHON_PROJECT@.glop':['*.pyi'],
'@PYTHON_PROJECT@.graph.python':[
'$',
'$',
- '$'
- ],
+ '$',
+ '*.pyi'],
'@PYTHON_PROJECT@.constraint_solver':['$', '*.pyi'],
'@PYTHON_PROJECT@.linear_solver':['$', '*.pyi'],
'@PYTHON_PROJECT@.linear_solver.python':['$', '*.pyi'],
'@PYTHON_PROJECT@.packing':['*.pyi'],
'@PYTHON_PROJECT@.pdlp':['*.pyi'],
- '@PYTHON_PROJECT@.pdlp.python':['$'],
+ '@PYTHON_PROJECT@.pdlp.python':['$', '*.pyi'],
'@PYTHON_PROJECT@.sat':['*.pyi'],
'@PYTHON_PROJECT@.sat.colab':['*.pyi'],
'@PYTHON_PROJECT@.sat.python':['$', '*.pyi'],