Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit ecf5f00

Browse filesBrowse 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
Copy full SHA for ecf5f00

File tree

Expand file treeCollapse file tree

2 files changed

+15
-81
lines changed
Filter options
Expand file treeCollapse file tree

2 files changed

+15
-81
lines changed

‎setup.py

Copy file name to clipboardExpand all lines: setup.py
+12-10Lines 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

Copy file name to clipboardExpand all lines: setupext.py
+3-71Lines 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 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.