123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291 |
- """distutils.core
- The only module that needs to be imported to use the Distutils; provides
- the 'setup' function (which is to be called from the setup script). Also
- indirectly provides the Distribution and Command classes, although they are
- really defined in distutils.dist and distutils.cmd.
- """
- import os
- import sys
- import tokenize
- from distutils.debug import DEBUG
- from distutils.errors import (
- DistutilsSetupError,
- DistutilsError,
- CCompilerError,
- DistutilsArgError,
- )
- # Mainly import these so setup scripts can "from distutils.core import" them.
- from distutils.dist import Distribution
- from distutils.cmd import Command
- from distutils.config import PyPIRCCommand
- from distutils.extension import Extension
- __all__ = ['Distribution', 'Command', 'PyPIRCCommand', 'Extension', 'setup']
- # This is a barebones help message generated displayed when the user
- # runs the setup script with no arguments at all. More useful help
- # is generated with various --help options: global help, list commands,
- # and per-command help.
- USAGE = """\
- usage: %(script)s [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...]
- or: %(script)s --help [cmd1 cmd2 ...]
- or: %(script)s --help-commands
- or: %(script)s cmd --help
- """
- def gen_usage(script_name):
- script = os.path.basename(script_name)
- return USAGE % locals()
- # Some mild magic to control the behaviour of 'setup()' from 'run_setup()'.
- _setup_stop_after = None
- _setup_distribution = None
- # Legal keyword arguments for the setup() function
- setup_keywords = (
- 'distclass',
- 'script_name',
- 'script_args',
- 'options',
- 'name',
- 'version',
- 'author',
- 'author_email',
- 'maintainer',
- 'maintainer_email',
- 'url',
- 'license',
- 'description',
- 'long_description',
- 'keywords',
- 'platforms',
- 'classifiers',
- 'download_url',
- 'requires',
- 'provides',
- 'obsoletes',
- )
- # Legal keyword arguments for the Extension constructor
- extension_keywords = (
- 'name',
- 'sources',
- 'include_dirs',
- 'define_macros',
- 'undef_macros',
- 'library_dirs',
- 'libraries',
- 'runtime_library_dirs',
- 'extra_objects',
- 'extra_compile_args',
- 'extra_link_args',
- 'swig_opts',
- 'export_symbols',
- 'depends',
- 'language',
- )
- def setup(**attrs): # noqa: C901
- """The gateway to the Distutils: do everything your setup script needs
- to do, in a highly flexible and user-driven way. Briefly: create a
- Distribution instance; find and parse config files; parse the command
- line; run each Distutils command found there, customized by the options
- supplied to 'setup()' (as keyword arguments), in config files, and on
- the command line.
- The Distribution instance might be an instance of a class supplied via
- the 'distclass' keyword argument to 'setup'; if no such class is
- supplied, then the Distribution class (in dist.py) is instantiated.
- All other arguments to 'setup' (except for 'cmdclass') are used to set
- attributes of the Distribution instance.
- The 'cmdclass' argument, if supplied, is a dictionary mapping command
- names to command classes. Each command encountered on the command line
- will be turned into a command class, which is in turn instantiated; any
- class found in 'cmdclass' is used in place of the default, which is
- (for command 'foo_bar') class 'foo_bar' in module
- 'distutils.command.foo_bar'. The command class must provide a
- 'user_options' attribute which is a list of option specifiers for
- 'distutils.fancy_getopt'. Any command-line options between the current
- and the next command are used to set attributes of the current command
- object.
- When the entire command-line has been successfully parsed, calls the
- 'run()' method on each command object in turn. This method will be
- driven entirely by the Distribution object (which each command object
- has a reference to, thanks to its constructor), and the
- command-specific options that became attributes of each command
- object.
- """
- global _setup_stop_after, _setup_distribution
- # Determine the distribution class -- either caller-supplied or
- # our Distribution (see below).
- klass = attrs.get('distclass')
- if klass:
- del attrs['distclass']
- else:
- klass = Distribution
- if 'script_name' not in attrs:
- attrs['script_name'] = os.path.basename(sys.argv[0])
- if 'script_args' not in attrs:
- attrs['script_args'] = sys.argv[1:]
- # Create the Distribution instance, using the remaining arguments
- # (ie. everything except distclass) to initialize it
- try:
- _setup_distribution = dist = klass(attrs)
- except DistutilsSetupError as msg:
- if 'name' not in attrs:
- raise SystemExit("error in setup command: %s" % msg)
- else:
- raise SystemExit("error in {} setup command: {}".format(attrs['name'], msg))
- if _setup_stop_after == "init":
- return dist
- # Find and parse the config file(s): they will override options from
- # the setup script, but be overridden by the command line.
- dist.parse_config_files()
- if DEBUG:
- print("options (after parsing config files):")
- dist.dump_option_dicts()
- if _setup_stop_after == "config":
- return dist
- # Parse the command line and override config files; any
- # command-line errors are the end user's fault, so turn them into
- # SystemExit to suppress tracebacks.
- try:
- ok = dist.parse_command_line()
- except DistutilsArgError as msg:
- raise SystemExit(gen_usage(dist.script_name) + "\nerror: %s" % msg)
- if DEBUG:
- print("options (after parsing command line):")
- dist.dump_option_dicts()
- if _setup_stop_after == "commandline":
- return dist
- # And finally, run all the commands found on the command line.
- if ok:
- 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: {}\n".format(exc))
- raise
- else:
- raise SystemExit("error: {}".format(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
- if you need to find out the distribution meta-data (passed as
- keyword args from 'script' to 'setup()', or the contents of the
- config files or command-line.
- 'script_name' is a file that will be read and run with 'exec()';
- 'sys.argv[0]' will be replaced with 'script' for the duration of the
- call. 'script_args' is a list of strings; if supplied,
- 'sys.argv[1:]' will be replaced by 'script_args' for the duration of
- the call.
- 'stop_after' tells 'setup()' when to stop processing; possible
- values:
- init
- stop after the Distribution instance has been created and
- populated with the keyword arguments to 'setup()'
- config
- stop after config files have been parsed (and their data
- stored in the Distribution instance)
- commandline
- stop after the command-line ('sys.argv[1:]' or 'script_args')
- have been parsed (and the data stored in the Distribution)
- run [default]
- stop after all commands have been run (the same as if 'setup()'
- had been called in the usual way
- Returns the Distribution instance, which provides all information
- used to drive the Distutils.
- """
- if stop_after not in ('init', 'config', 'commandline', 'run'):
- raise ValueError("invalid value for 'stop_after': {!r}".format(stop_after))
- global _setup_stop_after, _setup_distribution
- _setup_stop_after = stop_after
- save_argv = sys.argv.copy()
- g = {'__file__': script_name, '__name__': '__main__'}
- try:
- try:
- sys.argv[0] = script_name
- if script_args is not None:
- sys.argv[1:] = script_args
- # 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
- except SystemExit:
- # Hmm, should we do something if exiting with a non-zero code
- # (ie. error)?
- pass
- if _setup_distribution is None:
- raise RuntimeError(
- (
- "'distutils.core.setup()' was never called -- "
- "perhaps '%s' is not a Distutils setup script?"
- )
- % script_name
- )
- # I wonder if the setup script's namespace -- g and l -- would be of
- # any interest to callers?
- # print "_setup_distribution:", _setup_distribution
- return _setup_distribution
- # run_setup ()
|