Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit ecf5f00

Browse files
committed
Simplify extension setup.
setupext currently has a complex machinery to define extension modules. Essentially, the problem is that numpy may not be installed at the time the extensions are declared, so we can't call np.get_include() to get the include paths. So we use a DelayedExtension class and a hook mechanism to inject the numpy include path later, once it becomes available. Instead, we can just declare a dummy extension (so that setuptools doesn't elide the call to build_ext), then swap in the correct extensions in build_ext.finalize_options(): at that point, numpy will have been installed and we don't need any crazy machinery.
1 parent eb92f62 commit ecf5f00

File tree

2 files changed

+15
-81
lines changed

2 files changed

+15
-81
lines changed

setup.py

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
import urllib.request
2525
from zipfile import ZipFile
2626

27-
from setuptools import setup
27+
from setuptools import setup, Extension
2828
from setuptools.command.build_ext import build_ext as BuildExtCommand
2929
from setuptools.command.develop import develop as DevelopCommand
3030
from setuptools.command.install_lib import install_lib as InstallLibCommand
@@ -101,6 +101,12 @@ def __init__(self, dist):
101101

102102

103103
class BuildExtraLibraries(BuildExtCommand):
104+
def finalize_options(self):
105+
self.distribution.ext_modules[:] = [
106+
*filter(None, (package.get_extension()
107+
for package in good_packages))]
108+
super().finalize_options()
109+
104110
def build_extensions(self):
105111
# Remove the -Wstrict-prototypes option, it's not valid for C++. Fixed
106112
# in Py3.7 as bpo-5755.
@@ -171,7 +177,9 @@ def run(self):
171177
packages = []
172178
namespace_packages = []
173179
py_modules = []
174-
ext_modules = []
180+
# Dummy extension to trigger build_ext, which will swap it out with real
181+
# extensions that can depend on numpy for the build.
182+
ext_modules = [Extension('', [])]
175183
package_data = {}
176184
package_dir = {'': 'lib'}
177185
install_requires = []
@@ -230,9 +238,8 @@ def run(self):
230238
packages.extend(package.get_packages())
231239
namespace_packages.extend(package.get_namespace_packages())
232240
py_modules.extend(package.get_py_modules())
233-
ext = package.get_extension()
234-
if ext is not None:
235-
ext_modules.append(ext)
241+
# Extension modules only get added in build_ext, as numpy will have
242+
# been installed (as setup_requires) at that point.
236243
data = package.get_package_data()
237244
for key, val in data.items():
238245
package_data.setdefault(key, [])
@@ -252,11 +259,6 @@ def run(self):
252259
with open('lib/matplotlib/mpl-data/matplotlibrc', 'w') as fd:
253260
fd.write(''.join(template_lines))
254261

255-
# Finalize the extension modules so they can get the Numpy include
256-
# dirs
257-
for mod in ext_modules:
258-
mod.finalize()
259-
260262
# Finally, pass this all along to distutils to do the heavy lifting.
261263
setup(
262264
name="matplotlib",

setupext.py

Lines changed: 3 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,7 @@ def make_extension(name, files, *args, **kwargs):
319319
Any additional arguments are passed to the
320320
`distutils.core.Extension` constructor.
321321
"""
322-
ext = DelayedExtension(name, files, *args, **kwargs)
322+
ext = Extension(name, files, *args, **kwargs)
323323
for dir in get_base_dirs():
324324
include_dir = os.path.join(dir, 'include')
325325
if os.path.exists(include_dir):
@@ -329,7 +329,6 @@ def make_extension(name, files, *args, **kwargs):
329329
if os.path.exists(lib_dir):
330330
ext.library_dirs.append(lib_dir)
331331
ext.include_dirs.append('.')
332-
333332
return ext
334333

335334

@@ -789,79 +788,12 @@ def get_package_data(self):
789788
}
790789

791790

792-
class DelayedExtension(Extension, object):
793-
"""
794-
A distutils Extension subclass where some of its members
795-
may have delayed computation until reaching the build phase.
796-
797-
This is so we can, for example, get the Numpy include dirs
798-
after pip has installed Numpy for us if it wasn't already
799-
on the system.
800-
"""
801-
def __init__(self, *args, **kwargs):
802-
super().__init__(*args, **kwargs)
803-
self._finalized = False
804-
self._hooks = {}
805-
806-
def add_hook(self, member, func):
807-
"""
808-
Add a hook to dynamically compute a member.
809-
810-
Parameters
811-
----------
812-
member : string
813-
The name of the member
814-
815-
func : callable
816-
The function to call to get dynamically-computed values
817-
for the member.
818-
"""
819-
self._hooks[member] = func
820-
821-
def finalize(self):
822-
self._finalized = True
823-
824-
class DelayedMember(property):
825-
def __init__(self, name):
826-
self._name = name
827-
828-
def __get__(self, obj, objtype=None):
829-
result = getattr(obj, '_' + self._name, [])
830-
831-
if obj._finalized:
832-
if self._name in obj._hooks:
833-
result = obj._hooks[self._name]() + result
834-
835-
return result
836-
837-
def __set__(self, obj, value):
838-
setattr(obj, '_' + self._name, value)
839-
840-
include_dirs = DelayedMember('include_dirs')
841-
842-
843791
class Numpy(SetupPackage):
844792
name = "numpy"
845793

846-
@staticmethod
847-
def include_dirs_hook():
848-
if hasattr(builtins, '__NUMPY_SETUP__'):
849-
del builtins.__NUMPY_SETUP__
850-
import numpy
851-
importlib.reload(numpy)
852-
853-
ext = Extension('test', [])
854-
ext.include_dirs.append(numpy.get_include())
855-
if not has_include_file(
856-
ext.include_dirs, os.path.join("numpy", "arrayobject.h")):
857-
_log.warning(
858-
"The C headers for numpy could not be found. "
859-
"You may need to install the development package")
860-
861-
return [numpy.get_include()]
862-
863794
def add_flags(self, ext):
864-
ext.add_hook('include_dirs', self.include_dirs_hook)
795+
import numpy as np
796+
ext.include_dirs.append(np.get_include())
865797
ext.define_macros.extend([
866798
# Ensure that PY_ARRAY_UNIQUE_SYMBOL is uniquely defined for each
867799
# extension.

0 commit comments

Comments
 (0)