1- import importlib .util
21import json
32import os
4- from os .path import abspath , exists , dirname , join
3+ from os .path import exists , dirname , join
54import subprocess
65import sys
76
87import pybind11_stubgen
9- from setuptools import Command
10- from distutils .errors import DistutilsError
118
12- from .util import get_install_root
9+ try :
10+ from setuptools .errors import BaseError
11+ except ImportError :
12+ from distutils .errors import DistutilsError as BaseError
1313
14+ from ._built_env import _BuiltEnv , _PackageFinder
1415
15- class GeneratePyiError (DistutilsError ):
16+
17+ class GeneratePyiError (BaseError ):
1618 pass
1719
1820
19- class BuildPyi (Command ):
21+ class BuildPyi (_BuiltEnv ):
2022 base_package : str
2123
2224 command_name = "build_pyi"
2325 description = "Generates pyi files from built extensions"
2426
25- user_options = [("build-lib=" , "d" , 'directory to "build" (copy) to' )]
26-
27- def initialize_options (self ):
28- self .build_lib = None
29-
30- def finalize_options (self ):
31- self .set_undefined_options ("build" , ("build_lib" , "build_lib" ))
32-
3327 def run (self ):
3428 # cannot build pyi files when cross-compiling
3529 if (
@@ -40,50 +34,18 @@ def run(self):
4034 return
4135
4236 # Gather information for needed stubs
43- data = {"mapping" : {}, "stubs" : []}
44-
45- # OSX-specific: need to set DYLD_LIBRARY_PATH otherwise modules don't
46- # work. Luckily, that information was computed when building the
47- # extensions...
48- env = os .environ .copy ()
49- dyld_path = set ()
50-
51- # Requires information from build_ext to work
52- build_ext = self .distribution .get_command_obj ("build_ext" )
53- if build_ext .inplace :
54- data ["out" ] = get_install_root (self )
55- else :
56- data ["out" ] = self .build_lib
57-
58- # Ensure that the associated packages can always be found locally
59- for wrapper in build_ext .wrappers :
60- pkgdir = wrapper .package_name .split ("." )
61- init_py = abspath (join (self .build_lib , * pkgdir , "__init__.py" ))
62- if exists (init_py ):
63- data ["mapping" ][wrapper .package_name ] = init_py
37+ data , env = self .setup_built_env ()
38+ data ["stubs" ] = []
6439
6540 # Ensure that the built extension can always be found
41+ build_ext = self .get_finalized_command ("build_ext" )
6642 for ext in build_ext .extensions :
67- fname = build_ext .get_ext_filename (ext .name )
68- data ["mapping" ][ext .name ] = abspath (join (self .build_lib , fname ))
6943 data ["stubs" ].append (ext .name )
7044
71- rpybuild_libs = getattr (ext , "rpybuild_libs" , None )
72- if rpybuild_libs :
73- for pth , _ in rpybuild_libs .values ():
74- dyld_path .add (dirname (pth ))
75-
7645 # Don't do anything if nothing is needed
7746 if not data ["stubs" ]:
7847 return
7948
80- # OSX-specific
81- if dyld_path :
82- dyld_path = ":" .join (dyld_path )
83- if "DYLD_LIBRARY_PATH" in env :
84- dyld_path += ":" + env ["DYLD_LIBRARY_PATH" ]
85- env ["DYLD_LIBRARY_PATH" ] = dyld_path
86-
8749 data_json = json .dumps (data )
8850
8951 # Execute in a subprocess in case it crashes
@@ -101,45 +63,6 @@ def run(self):
10163 pass
10264
10365
104- class _PackageFinder :
105- """
106- Custom loader to allow loading built modules from their location
107- in the build directory (as opposed to their install location)
108- """
109-
110- mapping = {}
111-
112- @classmethod
113- def find_spec (cls , fullname , path , target = None ):
114- m = cls .mapping .get (fullname )
115- if m :
116- return importlib .util .spec_from_file_location (fullname , m )
117-
118-
119- def generate_pyi (module_name : str , pyi_filename : str ):
120- print ("generating" , pyi_filename )
121-
122- pybind11_stubgen .FunctionSignature .n_invalid_signatures = 0
123- module = pybind11_stubgen .ModuleStubsGenerator (module_name )
124- module .parse ()
125- if pybind11_stubgen .FunctionSignature .n_invalid_signatures > 0 :
126- print ("FAILED to generate pyi for" , module_name , file = sys .stderr )
127- return False
128-
129- module .write_setup_py = False
130- with open (pyi_filename , "w" ) as fp :
131- fp .write ("#\n # AUTOMATICALLY GENERATED FILE, DO NOT EDIT!\n #\n \n " )
132- fp .write ("\n " .join (module .to_lines ()))
133-
134- typed = join (dirname (pyi_filename ), "py.typed" )
135- print ("generating" , typed )
136- if not exists (typed ):
137- with open (typed , "w" ) as fp :
138- pass
139-
140- return True
141-
142-
14366def main ():
14467 cfg = json .load (sys .stdin )
14568
0 commit comments