Skip to content

Further simplify setupext. #16177

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 9, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 3 additions & 10 deletions setup.cfg.template
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,11 @@

[gui_support]
# Matplotlib supports multiple GUI toolkits, known as backends.
# The Mac OSX backend requires the Cocoa headers included with XCode.
# The MacOSX backend requires the Cocoa headers included with XCode.
# You can select whether to build it by uncommenting the following line.
# Acceptable values are:
# It is never built on Linux or Windows, regardless of the config value.
#
# True: build the extension. Exits with a warning if the
# required dependencies are not available
# False: do not build the extension
# auto: build if the required dependencies are available,
# otherwise skip silently. This is the default
# behavior
#
#macosx = auto
#macosx = True

[rc_options]
# User-configurable options
Expand Down
16 changes: 7 additions & 9 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,15 +187,13 @@ def run(self):
good_packages = []
for package in mpl_packages:
try:
result = package.check()
if result is not None:
print_status(package.name, 'yes [%s]' % result)
except setupext.CheckFailed as e:
print_status(package.name, 'no [%s]' % str(e))
if not package.optional:
sys.exit("Failed to build %s" % package.name)
else:
good_packages.append(package)
message = package.check()
except setupext.Skipped as e:
print_status(package.name, f"no [{e}]")
continue
if message is not None:
print_status(package.name, f"yes [{message}]")
good_packages.append(package)

print_raw()

Expand Down
54 changes: 17 additions & 37 deletions setupext.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,38 +256,38 @@ def pkg_config_setup_extension(
ext.libraries.extend(default_libraries)


class CheckFailed(Exception):
class Skipped(Exception):
"""
Exception thrown when a `SetupPackage.check` method fails.
Exception thrown by `SetupPackage.check` to indicate that a package should
be skipped.
"""
pass


class SetupPackage:
optional = False

def check(self):
"""
Checks whether the build dependencies are met. Should raise a
`CheckFailed` exception if the dependency could not be met, otherwise
return a string indicating a version number or some other message
indicating what was found.
If the package should be installed, return an informative string, or
None if no information should be displayed at all.

If the package should be skipped, raise a `Skipped` exception.

If a missing build dependency is fatal, call `sys.exit`.
"""
pass

def get_package_data(self):
"""
Get a package data dictionary to add to the configuration.
These are merged into to the `package_data` list passed to
`distutils.setup`.
These are merged into to the *package_data* list passed to
`setuptools.setup`.
"""
return {}

def get_extension(self):
"""
Get a list of C extensions (`distutils.core.Extension`
objects) to add to the configuration. These are added to the
`extensions` list passed to `distutils.setup`.
*extensions* list passed to `setuptools.setup`.
"""
return None

Expand All @@ -297,43 +297,23 @@ def do_custom_build(self):
third-party library, before building an extension, it should
override this method.
"""
pass


class OptionalPackage(SetupPackage):
optional = True
config_category = "packages"
default_config = "auto"

@classmethod
def get_config(cls):
"""
Look at `setup.cfg` and return one of ["auto", True, False] indicating
if the package is at default state ("auto"), forced by the user (case
insensitively defined as 1, true, yes, on for True) or opted-out (case
insensitively defined as 0, false, no, off for False).
"""
conf = cls.default_config
if config.has_option(cls.config_category, cls.name):
try:
conf = config.getboolean(cls.config_category, cls.name)
except ValueError:
conf = config.get(cls.config_category, cls.name)
return conf
default_config = True

def check(self):
"""
Check whether ``setup.cfg`` requests this package to be installed.

May be overridden by subclasses for additional checks.
"""
conf = self.get_config() # Check configuration file
if conf in [True, 'auto']: # Default "auto", or install forced by user
if conf is True: # Set non-optional if user sets `True` in config
self.optional = False
if config.getboolean(self.config_category, self.name,
fallback=self.default_config):
return "installing"
else: # Configuration opt-out by user
raise CheckFailed("skipping due to configuration")
raise Skipped("skipping due to configuration")


class Platform(SetupPackage):
Expand Down Expand Up @@ -745,7 +725,7 @@ class BackendMacOSX(OptionalPackage):

def check(self):
if sys.platform != 'darwin':
raise CheckFailed("Mac OS-X only")
raise Skipped("Mac OS-X only")
return super().check()

def get_extension(self):
Expand Down