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

Skip to content

Commit d8258ed

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 6646f52 commit d8258ed

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
@@ -7,7 +7,7 @@
77
# to ensure that we error out properly for people with outdated setuptools
88
# and/or pip.
99
import sys
10-
from setuptools import setup
10+
from setuptools import setup, Extension
1111
from setuptools.command.test import test as TestCommand
1212
from setuptools.command.build_ext import build_ext as BuildExtCommand
1313

@@ -94,6 +94,12 @@ def __init__(self, dist):
9494

9595

9696
class BuildExtraLibraries(BuildExtCommand):
97+
def finalize_options(self):
98+
self.distribution.ext_modules[:] = [
99+
*filter(None, (package.get_extension()
100+
for package in good_packages))]
101+
super().finalize_options()
102+
97103
def build_extensions(self):
98104
# Remove the -Wstrict-prototypes option, it's not valid for C++. Fixed
99105
# in Py3.7 as bpo-5755.
@@ -119,7 +125,9 @@ def build_extensions(self):
119125
packages = []
120126
namespace_packages = []
121127
py_modules = []
122-
ext_modules = []
128+
# Dummy extension to trigger build_ext, which will swap it out with real
129+
# extensions that can depend on numpy for the build.
130+
ext_modules = [Extension('', [])]
123131
package_data = {}
124132
package_dir = {'': 'lib'}
125133
install_requires = []
@@ -178,9 +186,8 @@ def build_extensions(self):
178186
packages.extend(package.get_packages())
179187
namespace_packages.extend(package.get_namespace_packages())
180188
py_modules.extend(package.get_py_modules())
181-
ext = package.get_extension()
182-
if ext is not None:
183-
ext_modules.append(ext)
189+
# Extension modules only get added in build_ext, as numpy will have
190+
# been installed (as setup_requires) at that point.
184191
data = package.get_package_data()
185192
for key, val in data.items():
186193
package_data.setdefault(key, [])
@@ -200,11 +207,6 @@ def build_extensions(self):
200207
with open('lib/matplotlib/mpl-data/matplotlibrc', 'w') as fd:
201208
fd.write(''.join(template_lines))
202209

203-
# Finalize the extension modules so they can get the Numpy include
204-
# dirs
205-
for mod in ext_modules:
206-
mod.finalize()
207-
208210
# Finally, pass this all along to distutils to do the heavy lifting.
209211
setup(
210212
name="matplotlib",

setupext.py

Lines changed: 3 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ def make_extension(name, files, *args, **kwargs):
203203
Any additional arguments are passed to the
204204
`distutils.core.Extension` constructor.
205205
"""
206-
ext = DelayedExtension(name, files, *args, **kwargs)
206+
ext = Extension(name, files, *args, **kwargs)
207207
for dir in get_base_dirs():
208208
include_dir = os.path.join(dir, 'include')
209209
if os.path.exists(include_dir):
@@ -213,7 +213,6 @@ def make_extension(name, files, *args, **kwargs):
213213
if os.path.exists(lib_dir):
214214
ext.library_dirs.append(lib_dir)
215215
ext.include_dirs.append('.')
216-
217216
return ext
218217

219218

@@ -702,79 +701,12 @@ def get_package_data(self):
702701
}
703702

704703

705-
class DelayedExtension(Extension, object):
706-
"""
707-
A distutils Extension subclass where some of its members
708-
may have delayed computation until reaching the build phase.
709-
710-
This is so we can, for example, get the Numpy include dirs
711-
after pip has installed Numpy for us if it wasn't already
712-
on the system.
713-
"""
714-
def __init__(self, *args, **kwargs):
715-
super().__init__(*args, **kwargs)
716-
self._finalized = False
717-
self._hooks = {}
718-
719-
def add_hook(self, member, func):
720-
"""
721-
Add a hook to dynamically compute a member.
722-
723-
Parameters
724-
----------
725-
member : string
726-
The name of the member
727-
728-
func : callable
729-
The function to call to get dynamically-computed values
730-
for the member.
731-
"""
732-
self._hooks[member] = func
733-
734-
def finalize(self):
735-
self._finalized = True
736-
737-
class DelayedMember(property):
738-
def __init__(self, name):
739-
self._name = name
740-
741-
def __get__(self, obj, objtype=None):
742-
result = getattr(obj, '_' + self._name, [])
743-
744-
if obj._finalized:
745-
if self._name in obj._hooks:
746-
result = obj._hooks[self._name]() + result
747-
748-
return result
749-
750-
def __set__(self, obj, value):
751-
setattr(obj, '_' + self._name, value)
752-
753-
include_dirs = DelayedMember('include_dirs')
754-
755-
756704
class Numpy(SetupPackage):
757705
name = "numpy"
758706

759-
@staticmethod
760-
def include_dirs_hook():
761-
if hasattr(builtins, '__NUMPY_SETUP__'):
762-
del builtins.__NUMPY_SETUP__
763-
import numpy
764-
importlib.reload(numpy)
765-
766-
ext = Extension('test', [])
767-
ext.include_dirs.append(numpy.get_include())
768-
if not has_include_file(
769-
ext.include_dirs, os.path.join("numpy", "arrayobject.h")):
770-
_log.warning(
771-
"The C headers for numpy could not be found. "
772-
"You may need to install the development package")
773-
774-
return [numpy.get_include()]
775-
776707
def add_flags(self, ext):
777-
ext.add_hook('include_dirs', self.include_dirs_hook)
708+
import numpy as np
709+
ext.include_dirs.append(np.get_include())
778710
ext.define_macros.extend([
779711
# Ensure that PY_ARRAY_UNIQUE_SYMBOL is uniquely defined for each
780712
# extension.

0 commit comments

Comments
 (0)