views:

109

answers:

2

I'd like to call Python's distutils' or setuptools' setup() function in a slightly unconventional way, but I'm not sure whether distutils is meant for this kind of usage.

As an example, let's say I currently have a 'setup.py' file, which looks like this (lifted verbatim from the distutils docs--the setuptools usage is almost identical):

from distutils.core import setup

setup(name='Distutils',
      version='1.0',
      description='Python Distribution Utilities',
      author='Greg Ward',
      author_email='[email protected]',
      url='http://www.python.org/sigs/distutils-sig/',
      packages=['distutils', 'distutils.command'],
     )

Normally, to build just the .spec file for an RPM of this module, I could run python setup.py bdist_rpm --spec-only, which parses the command line and calls the 'bdist_rpm' code to handle the RPM-specific stuff. The .spec file ends up in './dist'.

How can I change my setup() invocation so that it runs the 'bdist_rpm' command with the '--spec-only' option, WITHOUT parsing command-line parameters? Can I pass the command name and options as parameters to setup()? Or can I manually construct a command line, and pass that as a parameter, instead?

NOTE: I already know that I could call the script in a separate process, with an actual command line, using os.system() or the subprocess module or something similar. I'm trying to avoid using any kind of external command invocations. I'm looking specifically for a solution that runs setup() in the current interpreter.

For background, I'm converting some release-management shell scripts into a single Python program. One of the tasks is running 'setup.py' to generate a .spec file for further pre-release testing. Running 'setup.py' as an external command, with its own command line options, seems like an awkward method, and it complicates the rest of the program. I feel like there may be a more Pythonic way.

+1  A: 

Never tried this, but I did happen to look in distutils/core.py, where I notice this near the start of setup():

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:]

So, it looks as if you can "fake-out" setup() by adding:

setup(
    ...
    script_name = 'setup.py',
    script_args = ['bdist_rpm', '--spec-only']
)
ig0774
@ig0775: Thanks, that's exactly what I wanted to do. I just tested it, and it works fine. Pretty clean looking, too.
Ryan B. Lynch
A: 

Just "fake" the commandline parameters -- e.g, start you script with

import sys

sys.argv[1:] = ['bdist_rpm', '--spec-only']

from distutils.core import setup

setup(name='Distutils',

etc, etc. After all, that's how distutils gets the command line parameters: it looks in sys.argv! So, just set sys.argv to be exactly as you want it, and whatever command line the misguided user typed will be completely ignored.

Actually, you might want to check if the user did enter any argument you're about to ignore -- len(sys.argv) > 1 before you modify sys.argv -- and give a warning, or avoid the alteration of sys.argv, or "merge" what the user typed, etc... but that's quite different from what you actually asked, so I'm going to leave it at that;-).

Alex Martelli
This had occurred to me, but it seemed kind of messy. But if it weren't for @ig0774's method, this would probably be how I'd handle it.
Ryan B. Lynch