1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-29 15:23:05 +04:00

Clean up stale typing stubs during incremental builds

When a module is disabled in an incremental build (e.g. switching from
-DBUILD_opencv_gapi=ON to OFF), its typing stub directory persists from
the previous build.  The stubs generator only creates directories for
enabled modules but never removes old ones, and the copy step merges
rather than replaces, so stale .pyi files propagate to both the loader
directory and the install prefix.

This causes type-checkers (mypy, pyright) to report errors for stubs
that reference symbols from modules that are no longer available.

Fix both propagation paths:

- generation.py: remove all subdirectories under the stubs output root
  before regenerating, so only currently enabled module stubs exist in
  the build directory.  Top-level files (py.typed) are preserved.

- copy_typings_stubs_on_success.py: remove stale .pyi files and
  py.typed markers from the loader directory before copying fresh stubs,
  so leftover stubs from a previous copy are cleaned up.  Runtime .py
  files are not affected.
This commit is contained in:
Pavel Guzenfeld
2026-03-16 23:41:37 +02:00
parent 2dd3d1371a
commit 3779f630bc
2 changed files with 40 additions and 0 deletions
@@ -13,6 +13,21 @@ else:
from distutils.dir_util import copy_tree
def _remove_stale_pyi_files(directory):
"""Remove .pyi files and py.typed markers from the directory tree.
During incremental builds, disabling a previously enabled module leaves
stale typing stubs in the loader directory from a previous copy. Since
copy_tree merges rather than replaces, those stale files persist.
Removing all stub files before copying ensures only stubs for currently
enabled modules are present. Runtime .py files are not affected.
"""
for dirpath, dirnames, filenames in os.walk(directory):
for fname in filenames:
if fname.endswith('.pyi') or fname == 'py.typed':
os.remove(os.path.join(dirpath, fname))
def main():
args = parse_arguments()
py_typed_path = os.path.join(args.stubs_dir, 'py.typed')
@@ -24,6 +39,8 @@ def main():
'generation phase.'.format(py_typed_path)
)
return
if os.path.isdir(args.output_dir):
_remove_stale_pyi_files(args.output_dir)
copy_tree(args.stubs_dir, args.output_dir)
@@ -3,6 +3,7 @@ __all__ = ("generate_typing_stubs", )
from io import StringIO
from pathlib import Path
import re
import shutil
from typing import (Callable, NamedTuple, Union, Set, Dict,
Collection, Tuple, List)
import warnings
@@ -24,6 +25,22 @@ from .nodes.type_node import (TypeNode, AliasTypeNode, AliasRefTypeNode,
ConditionalAliasTypeNode, PrimitiveTypeNode)
def _clean_stale_stubs_dirs(stubs_root: Path) -> None:
"""Remove all subdirectories under stubs_root.
During incremental builds, disabling a previously enabled module leaves
behind its typing stub directory (e.g. cv2/gapi/). Removing all
subdirectories before regeneration ensures only stubs for currently
enabled modules are present. Top-level files (py.typed, __init__.pyi)
are kept because they are managed separately.
"""
if not stubs_root.is_dir():
return
for item in stubs_root.iterdir():
if item.is_dir():
shutil.rmtree(item)
def generate_typing_stubs(root: NamespaceNode, output_path: Path):
"""Generates typing stubs for the AST with root `root` and outputs
created files tree to directory pointed by `output_path`.
@@ -88,6 +105,12 @@ def generate_typing_stubs(root: NamespaceNode, output_path: Path):
# The whole process should fail !only! when all possible scopes are
# checked and at least 1 node is still unresolved.
root.resolve_type_nodes()
# Remove stale typing stub subdirectories from previous builds.
# In incremental builds, disabling a module (e.g. -DBUILD_opencv_gapi=OFF)
# no longer generates its stubs, but leftover directories from a previous
# build persist and propagate through the copy/install steps, causing
# type-checker errors for stubs referencing unavailable modules.
_clean_stale_stubs_dirs(Path(output_path) / root.export_name)
_generate_typing_module(root, output_path)
_populate_reexported_symbols(root)
_generate_typing_stubs(root, output_path)