summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDonald Stufft <donald@stufft.io>2014-09-29 14:28:04 -0400
committerDonald Stufft <donald@stufft.io>2014-09-29 14:28:04 -0400
commit1e9e7bd2989056d66c19ec61d2c6b6dde0e587ea (patch)
tree348023ccaab42712198bf4bae5276a72c7939e79
parent147805a901f2c848501edbbd1daa5a129d903d04 (diff)
parent97f45302f16c64c5e3af1a895f4906857f3a5100 (diff)
downloadcryptography-1e9e7bd2989056d66c19ec61d2c6b6dde0e587ea.tar.gz
Merge pull request #1366 from reaperhulk/rebase-setup-fix
Rebased work around for side effects in setup.py script
-rw-r--r--setup.py155
1 files changed, 147 insertions, 8 deletions
diff --git a/setup.py b/setup.py
index 347dbe822..ca7292757 100644
--- a/setup.py
+++ b/setup.py
@@ -140,6 +140,151 @@ class PyTest(test):
sys.exit(errno)
+def keywords_with_side_effects(argv):
+ """
+ Get a dictionary with setup keywords that (can) have side effects.
+
+ :param argv: A list of strings with command line arguments.
+ :returns: A dictionary with keyword arguments for the ``setup()`` function.
+
+ This setup.py script uses the setuptools 'setup_requires' feature because
+ this is required by the cffi package to compile extension modules. The
+ purpose of ``keywords_with_side_effects()`` is to avoid triggering the cffi
+ build process as a result of setup.py invocations that don't need the cffi
+ module to be built (setup.py serves the dual purpose of exposing package
+ metadata).
+
+ All of the options listed by ``python setup.py --help`` that print
+ information should be recognized here. The commands ``clean``,
+ ``egg_info``, ``register``, ``sdist`` and ``upload`` are also recognized.
+ Any combination of these options and commands is also supported.
+
+ This function was originally based on the `setup.py script`_ of SciPy (see
+ also the discussion in `pip issue #25`_).
+
+ .. _pip issue #25: https://github.com/pypa/pip/issues/25
+ .. _setup.py script: https://github.com/scipy/scipy/blob/master/setup.py
+ """
+ no_setup_requires_arguments = (
+ '-h', '--help',
+ '-n', '--dry-run',
+ '-q', '--quiet',
+ '-v', '--verbose',
+ '-V', '--version',
+ '--author',
+ '--author-email',
+ '--classifiers',
+ '--contact',
+ '--contact-email',
+ '--description',
+ '--egg-base',
+ '--fullname',
+ '--help-commands',
+ '--keywords',
+ '--licence',
+ '--license',
+ '--long-description',
+ '--maintainer',
+ '--maintainer-email',
+ '--name',
+ '--no-user-cfg',
+ '--obsoletes',
+ '--platforms',
+ '--provides',
+ '--requires',
+ '--url',
+ 'clean',
+ 'egg_info',
+ 'register',
+ 'sdist',
+ 'upload',
+ )
+
+ def is_short_option(argument):
+ """Check whether a command line argument is a short option."""
+ return len(argument) >= 2 and argument[0] == '-' and argument[1] != '-'
+
+ def expand_short_options(argument):
+ """Expand combined short options into canonical short options."""
+ return ('-' + char for char in argument[1:])
+
+ def argument_without_setup_requirements(argv, i):
+ """Check whether a command line argument needs setup requirements."""
+ if argv[i] in no_setup_requires_arguments:
+ # Simple case: An argument which is either an option or a command
+ # which doesn't need setup requirements.
+ return True
+ elif (is_short_option(argv[i]) and
+ all(option in no_setup_requires_arguments
+ for option in expand_short_options(argv[i]))):
+ # Not so simple case: Combined short options none of which need
+ # setup requirements.
+ return True
+ elif argv[i - 1:i] == ['--egg-base']:
+ # Tricky case: --egg-info takes an argument which should not make
+ # us use setup_requires (defeating the purpose of this code).
+ return True
+ else:
+ return False
+
+ if all(argument_without_setup_requirements(argv, i)
+ for i in range(1, len(argv))):
+ return {
+ "cmdclass": {
+ "build": DummyCFFIBuild,
+ "install": DummyCFFIInstall,
+ "test": DummyPyTest,
+ }
+ }
+ else:
+ return {
+ "setup_requires": requirements,
+ "cmdclass": {
+ "build": CFFIBuild,
+ "install": CFFIInstall,
+ "test": PyTest,
+ }
+ }
+
+
+setup_requires_error = ("Requested setup command that needs 'setup_requires' "
+ "while command line arguments implied a side effect "
+ "free command or option.")
+
+
+class DummyCFFIBuild(build):
+ """
+ This class makes it very obvious when ``keywords_with_side_effects()`` has
+ incorrectly interpreted the command line arguments to ``setup.py build`` as
+ one of the 'side effect free' commands or options.
+ """
+
+ def run(self):
+ raise RuntimeError(setup_requires_error)
+
+
+class DummyCFFIInstall(install):
+ """
+ This class makes it very obvious when ``keywords_with_side_effects()`` has
+ incorrectly interpreted the command line arguments to ``setup.py install``
+ as one of the 'side effect free' commands or options.
+ """
+
+ def run(self):
+ raise RuntimeError(setup_requires_error)
+
+
+class DummyPyTest(test):
+ """
+ This class makes it very obvious when ``keywords_with_side_effects()`` has
+ incorrectly interpreted the command line arguments to ``setup.py test`` as
+ one of the 'side effect free' commands or options.
+ """
+
+ def run_tests(self):
+ raise RuntimeError(setup_requires_error)
+
+
with open(os.path.join(base_dir, "README.rst")) as f:
long_description = f.read()
@@ -182,19 +327,13 @@ setup(
include_package_data=True,
install_requires=requirements,
- setup_requires=requirements,
tests_require=test_requirements,
# for cffi
zip_safe=False,
ext_package="cryptography",
- cmdclass={
- "build": CFFIBuild,
- "install": CFFIInstall,
- "test": PyTest,
- },
-
entry_points={
"cryptography.backends": backends,
- }
+ },
+ **keywords_with_side_effects(sys.argv)
)