diff --git a/distutils/core.py b/distutils/core.py index d603d4a4..f43888ea 100644 --- a/distutils/core.py +++ b/distutils/core.py @@ -8,6 +8,7 @@ import os import sys +import tokenize from distutils.debug import DEBUG from distutils.errors import * @@ -144,29 +145,41 @@ class found in 'cmdclass' is used in place of the default, which is # And finally, run all the commands found on the command line. if ok: - try: - dist.run_commands() - except KeyboardInterrupt: - raise SystemExit("interrupted") - except OSError as exc: - if DEBUG: - sys.stderr.write("error: %s\n" % (exc,)) - raise - else: - raise SystemExit("error: %s" % (exc,)) - - except (DistutilsError, - CCompilerError) as msg: - if DEBUG: - raise - else: - raise SystemExit("error: " + str(msg)) + return run_commands(dist) return dist # setup () +def run_commands (dist): + """Given a Distribution object run all the commands, + raising ``SystemExit`` errors in the case of failure. + + This function assumes that either ``sys.argv`` or ``dist.script_args`` + is already set accordingly. + """ + try: + dist.run_commands() + except KeyboardInterrupt: + raise SystemExit("interrupted") + except OSError as exc: + if DEBUG: + sys.stderr.write("error: %s\n" % (exc,)) + raise + else: + raise SystemExit("error: %s" % (exc,)) + + except (DistutilsError, + CCompilerError) as msg: + if DEBUG: + raise + else: + raise SystemExit("error: " + str(msg)) + + return dist + + def run_setup (script_name, script_args=None, stop_after="run"): """Run a setup script in a somewhat controlled environment, and return the Distribution instance that drives things. This is useful @@ -205,14 +218,16 @@ def run_setup (script_name, script_args=None, stop_after="run"): _setup_stop_after = stop_after save_argv = sys.argv.copy() - g = {'__file__': script_name} + g = {'__file__': script_name, '__name__': '__main__'} try: try: sys.argv[0] = script_name if script_args is not None: sys.argv[1:] = script_args - with open(script_name, 'rb') as f: - exec(f.read(), g) + # tokenize.open supports automatic encoding detection + with tokenize.open(script_name) as f: + code = f.read().replace(r'\r\n', r'\n') + exec(code, g) finally: sys.argv = save_argv _setup_stop_after = None diff --git a/distutils/tests/test_core.py b/distutils/tests/test_core.py index 666ff4a3..d99cfd26 100644 --- a/distutils/tests/test_core.py +++ b/distutils/tests/test_core.py @@ -10,6 +10,7 @@ import unittest from distutils.tests import support from distutils import log +from distutils.dist import Distribution # setup script that uses __file__ setup_using___file__ = """\ @@ -45,6 +46,16 @@ class install(_install): setup(cmdclass={'install': install}) """ +setup_within_if_main = """\ +from distutils.core import setup + +def main(): + return setup(name="setup_within_if_main") + +if __name__ == "__main__": + main() +""" + class CoreTestCase(support.EnvironGuard, unittest.TestCase): def setUp(self): @@ -115,6 +126,20 @@ def test_run_setup_uses_current_dir(self): output = output[:-1] self.assertEqual(cwd, output) + def test_run_setup_within_if_main(self): + dist = distutils.core.run_setup( + self.write_setup(setup_within_if_main), stop_after="config") + self.assertIsInstance(dist, Distribution) + self.assertEqual(dist.get_name(), "setup_within_if_main") + + def test_run_commands(self): + sys.argv = ['setup.py', 'build'] + dist = distutils.core.run_setup( + self.write_setup(setup_within_if_main), stop_after="commandline") + self.assertNotIn('build', dist.have_run) + distutils.core.run_commands(dist) + self.assertIn('build', dist.have_run) + def test_debug_mode(self): # this covers the code called when DEBUG is set sys.argv = ['setup.py', '--name']