Skip to content

Commit b512e49

Browse files
committed
Install numpy as a setup_requires dependency so pip will install Numpy first
1 parent ccaac6e commit b512e49

File tree

2 files changed

+99
-15
lines changed

2 files changed

+99
-15
lines changed

setup.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@
4646
else:
4747
del sdist.sdist.make_release_tree
4848

49+
from distutils.dist import Distribution
50+
4951
import setupext
5052
from setupext import print_line, print_raw, print_message, print_status
5153

@@ -126,6 +128,7 @@
126128
package_data = {}
127129
package_dir = {'': 'lib'}
128130
install_requires = []
131+
setup_requires = []
129132
default_backend = None
130133

131134

@@ -188,6 +191,7 @@
188191
package_data.setdefault(key, [])
189192
package_data[key] = list(set(val + package_data[key]))
190193
install_requires.extend(package.get_install_requires())
194+
setup_requires.extend(package.get_setup_requires())
191195

192196
# Write the default matplotlibrc file
193197
if default_backend is None:
@@ -213,6 +217,19 @@
213217
# versions of distribute don't support it.
214218
extra_args['use_2to3'] = True
215219

220+
# Finalize the extension modules so they can get the Numpy include
221+
# dirs
222+
for mod in ext_modules:
223+
mod.finalize()
224+
225+
226+
# Avoid installing setup_requires dependencies if the user just
227+
# queries for information
228+
if (any('--' + opt in sys.argv for opt in
229+
Distribution.display_option_names + ['help']) or
230+
'clean' in sys.argv):
231+
setup_requires = []
232+
216233

217234
# Finally, pass this all along to distutils to do the heavy lifting.
218235
distrib = setup(name="matplotlib",

setupext.py

Lines changed: 82 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ def make_extension(name, files, *args, **kwargs):
214214
Any additional arguments are passed to the
215215
`distutils.core.Extension` constructor.
216216
"""
217-
ext = Extension(name, files, *args, **kwargs)
217+
ext = DelayedExtension(name, files, *args, **kwargs)
218218
for dir in get_base_dirs():
219219
include_dir = os.path.join(dir, 'include')
220220
if os.path.exists(include_dir):
@@ -390,6 +390,14 @@ def get_install_requires(self):
390390
"""
391391
return []
392392

393+
def get_setup_requires(self):
394+
"""
395+
Get a list of Python packages that we require at build time.
396+
pip/easy_install will attempt to download and install this
397+
package if it is not installed.
398+
"""
399+
return []
400+
393401
def _check_for_pkg_config(self, package, include_file, min_version=None,
394402
version=None):
395403
"""
@@ -647,42 +655,101 @@ def get_install_requires(self):
647655
return ['nose']
648656

649657

658+
class DelayedExtension(Extension, object):
659+
"""
660+
A distutils Extension subclass where some of its members
661+
may have delayed computation until reaching the build phase.
662+
663+
This is so we can, for example, get the Numpy include dirs
664+
after pip has installed Numpy for us if it wasn't already
665+
on the system.
666+
"""
667+
def __init__(self, *args, **kwargs):
668+
super(DelayedExtension, self).__init__(*args, **kwargs)
669+
self._finalized = False
670+
self._hooks = {}
671+
672+
def add_hook(self, member, func):
673+
"""
674+
Add a hook to dynamically compute a member.
675+
676+
Parameters
677+
----------
678+
member : string
679+
The name of the member
680+
681+
func : callable
682+
The function to call to get dynamically-computed values
683+
for the member.
684+
"""
685+
self._hooks[member] = func
686+
687+
def finalize(self):
688+
self._finalized = True
689+
690+
class DelayedMember(property):
691+
def __init__(self, name):
692+
self._name = name
693+
694+
def __get__(self, obj, objtype=None):
695+
result = getattr(obj, '_' + self._name, [])
696+
697+
if obj._finalized:
698+
if self._name in obj._hooks:
699+
result = obj._hooks[self._name]() + result
700+
701+
return result
702+
703+
def __set__(self, obj, value):
704+
setattr(obj, '_' + self._name, value)
705+
706+
include_dirs = DelayedMember('include_dirs')
707+
708+
650709
class Numpy(SetupPackage):
651710
name = "numpy"
652711

712+
@staticmethod
713+
def include_dirs_hook():
714+
import numpy
715+
716+
ext = Extension('test', [])
717+
ext.include_dirs.append(numpy.get_include())
718+
if not has_include_file(
719+
ext.include_dirs, os.path.join("numpy", "arrayobject.h")):
720+
warnings.warn(
721+
"The C headers for numpy could not be found. "
722+
"You may need to install the development package")
723+
724+
return [numpy.get_include()]
725+
653726
def check(self):
654727
min_version = extract_versions()['__version__numpy__']
655728
try:
656729
import numpy
657730
except ImportError:
658-
raise SystemExit(
659-
"Requires numpy %s or later to build. (Numpy not found)" %
660-
min_version)
731+
return 'not found. pip may install it below.'
661732

662733
if not is_min_version(numpy.__version__, min_version):
663734
raise SystemExit(
664735
"Requires numpy %s or later to build. (Found %s)" %
665736
(min_version, numpy.__version__))
666737

667-
ext = make_extension('test', [])
668-
ext.include_dirs.append(numpy.get_include())
669-
if not has_include_file(
670-
ext.include_dirs, os.path.join("numpy", "arrayobject.h")):
671-
raise CheckFailed(
672-
"The C headers for numpy could not be found. You"
673-
"may need to install the development package.")
674-
675738
return 'version %s' % numpy.__version__
676739

677740
def add_flags(self, ext):
678-
import numpy
679-
680741
# Ensure that PY_ARRAY_UNIQUE_SYMBOL is uniquely defined for
681742
# each extension
682743
array_api_name = 'MPL_' + ext.name.replace('.', '_') + '_ARRAY_API'
683744

684-
ext.include_dirs.append(numpy.get_include())
685745
ext.define_macros.append(('PY_ARRAY_UNIQUE_SYMBOL', array_api_name))
746+
ext.add_hook('include_dirs', self.include_dirs_hook)
747+
748+
def get_setup_requires(self):
749+
return ['numpy>=1.5']
750+
751+
def get_install_requires(self):
752+
return ['numpy>=1.5']
686753

687754

688755
class CXX(SetupPackage):

0 commit comments

Comments
 (0)