27

One thing I hate about distutils (I guess he is the evil who does this) is that it changes the shebang line. In other words, the more rational and environment-vars decided scripture

#!/usr/bin/env python

gets magically converted into

#!/whatever/absolute/path/is/my/python

This is seen also with grok: I used grokproject in a virtualenv to start my project, but now I cannot move the development directory around anymore, because it puts absolute paths in the shebang directive.

The reason why I ask this is twofold

  • I want to move it around because I started developing in one directory (Experiments) and now I want to move it into a proper path, but I could not do it. So I created a new virtualenv and grokproject and copied my files. That fixes the issue, but leaves my curiosity for a more rational solution unsatisfied. In particular, if the reference to the virtualenv python interpreter was relative, the problem would not have been present in the first place. You know the layout of the virtualenv, and you can refer to the virtualenv python easily.
  • The second reason is that I would like to be able to scp the virtualenv to another computer and run it there without trouble. This is not possible if you have hardcoded paths.
wim
  • 338,267
  • 99
  • 616
  • 750
Stefano Borini
  • 138,652
  • 96
  • 297
  • 431
  • 4
    @Lennart: Because I find rather annoying that all my installed stuff has a very strong and painful dependency against a specific path of my filesystem, in particular when an alternative exists. – Stefano Borini Oct 07 '09 at 10:45
  • So you want to move it around, only because it's not obvious how? :-) Oooookay... – Lennart Regebro Oct 07 '09 at 10:51
  • I'm struggling with how to explain this, it because so obvious after a while. :) The way to do this is to create a python package (which you obviously have done) and maintain it in a version control system, such as subversion. Each time you want to install it anywhere, you will have to run setup.py. Before that it just isn't installed. You can't move installations between different python installations and machines, that will simply not work. – Lennart Regebro Oct 07 '09 at 11:03
  • @SilentGhost: sorry. I now realized that I wrote it wrong the first time, then went back saying to myself "I wrote it wrong", and found the fixed version. I didn't see someone else fixed it, I assumed it was mine and I just had to swap from the current form to the other form in order to correct it. With this I basically reverted it. – Stefano Borini Oct 07 '09 at 11:04
  • And you cans copy a virtualenv to another machine. It just doesn't make sense. That will only work if both machines are configured exactly alike (and in that case this will work too). – Lennart Regebro Oct 07 '09 at 11:05
  • @Lennart: yes, but in this case it's different. What I am "installing" is a virtual environment that is supposed to be self contained and likely to be moved around. Installation is a different story, but when I deploy a virtualenv, or layout a grokproject for the first time, I am not installing anything. I am deploying my development environment. – Stefano Borini Oct 07 '09 at 11:07
  • @Lennart: I read your answer here: http://stackoverflow.com/questions/1130402/virtualenv-with-all-python-libraries . From this point of view, you are right. Still, I don't understand the reason why my development environment must be so strict on the absolute position of itself within the filesystem. – Stefano Borini Oct 07 '09 at 11:10
  • 1. The purpose of buildout is so you can easily replicate your environment. Copying a whole environment after you have run buildout simply makes no sense. It defeats the whole purpose of the buildout, and is likely to fail. 2. And you *are* installing things. You are installing the python modules and grok, and everything else, into your virtual python. This happens when you run the buildout. 3. The development environment is strict on the position simply because there are no reason for it not to be. All you need to do is move it, rerun bootstrap and buildout, and you are done. – Lennart Regebro Oct 07 '09 at 11:22
  • It's also worth noticing that virtualenv and buildout partly does similar things. Buildout will never install into the system python, but installs all libraries locally, and thereby you isolate the main python from the buildout. Using virtualenv is therefore inly useful if you want to also isolate the buildout from the main python. – Lennart Regebro Oct 07 '09 at 11:27

5 Answers5

15

Of course you can move the development directory around. Distutils changes the paths to the python that you should run with when you run it. It's in Grok run when you run the buildout. Move and re-run the bootstrap and the buildout. Done!

Distutils changes the path to the Python you use to run distutils with. If it didn't, then you might end up installing a library in one python version, but when you try to run the script it would fail, because it would run with another python version that didn't have the library.

That's not insanity, it's in fact the only sane way to do it.

Update: If you know what you are doing, you can do this:

/path/to/install/python setup.py build -e "/the/path/you/want/python" install

Make sure you clean the build directory first though. :)

wim
  • 338,267
  • 99
  • 616
  • 750
Lennart Regebro
  • 167,292
  • 41
  • 224
  • 251
  • You have a point. However, I think there should be the possibility to skip this transformation, if you really want to. – Stefano Borini Oct 07 '09 at 10:51
  • 1
    It can, if you install it as data files instead of as a script. But this will break as soon as you install it on a system which doesn't look exactly like yours. – Lennart Regebro Oct 07 '09 at 10:58
13

Distutils will automatically replace the shebang with the location of the Python binary that was used to execute setup.py. To override this behavior you have two options:

Option 1: Manually

You may pass the flag --executable=/path/to/my/python to setup.py. Arguments are accepted.

Example:

% python setup.py build --executable=/opt/local/bin/python -d

Option 2: Automatically

Your other option is to add a line to setup.cfg. If you aren't using setup.cfg, create it in the same directory as setup.py. Setup.py looks for this on startup. Any options specified here can still be overridden with flags at the command-line.

% cat setup.cfg 
[build]
executable = /opt/local/bin/python -d
jathanism
  • 33,067
  • 9
  • 68
  • 86
  • Thanks for this. This is awesome. I can't find this documented anywhere. Care to post some documentation links? – Anand Jun 08 '13 at 03:52
  • `python setup.py build --help` ;) – jathanism Jun 11 '13 at 05:19
  • Is there something similar for `python setup.py develop`? My virtualenv setup results in some very long shebang paths that cause `bad interpreter` errors when trying to run them. I tried `sys.executable = '/usr/bin/env python'` from within the script, but the space seems to cause distutils to put quotes into the shebang, which breaks things in a different way. – ex-nerd Aug 31 '16 at 18:37
2

In one of the latest versions of distutils, there is a flag --no-autoreq that have worked for me:

--no-autoreq         do not automatically calculate dependencies

In my case, I was creating RPM files with python2.4 executable, in a server with both 2.4 and 2.6 installations. bdist just left the shebangs as they were, after running:

python setup.py bdist_rpm --no-autoreq

In the case that you are handling the spec files, you may use the solution explained at https://stackoverflow.com/a/7423994/722997, adding:

AutoReq: no
Community
  • 1
  • 1
edrabc
  • 1,259
  • 1
  • 14
  • 23
2

I have no solution to your problem, but I do see some rationale for the current behaviour of distutils.

#!/usr/bin/env python executes the system's default Python version. That is fine as long as your code is compatible with said version. When the default version is updated (from 2.5 to 3, say) your code or other Python code which references /usr/bin/env may stop working, even though the old Python version is still installed. For that reason it makes sense to "hardcode" the path to the appropriate python interpreter.

Edit: you are correct in asserting that specifying python2.4 or similar solves this problem.

Edit 2: things are not as clear cut when multiple installations of the same Python version are present, as Ned Deily points out in the comments below.

Community
  • 1
  • 1
Stephan202
  • 59,965
  • 13
  • 127
  • 133
  • you can use python24. The fact is that when you have a virtualenv, you would like to be able to move it around if you decide so. A relative path would be better, since when you create the virtualenv, everything is contained in that dir, and everything can be referred relative. – Stefano Borini Oct 07 '09 at 10:32
  • I have no experience with distutils. Your question quotes `#!/usr/bin/env python` instead of `#!/usr/bin/env python2.4`. Which of these did you specify in your code? Does distutils also perform the rewrite if the version number is specified? – Stephan202 Oct 07 '09 at 10:37
  • On some systems it is not unusual to have more than one instance of the *same* version of python installed; on this system, I happen to have 3 versions of python2.5 and that's not counting virtualenvs. As Lennart points out, scripts are installed to and tied to a particular interpreter installation. That's why, in the most general case, relative shebang paths don't guarantee correct behavior. – Ned Deily Oct 07 '09 at 18:05
  • @Ned: thanks for your reply! I hadn't considered the possibility of multiple installations of the same Python version. – Stephan202 Oct 07 '09 at 18:51
2

had the same issue. tried to find a way to prevent the touching altogether by default. here is the solution. essentially we override the default script copy routine (build_scripts).

in setup.py add

from distutils.command.build_scripts import build_scripts

# don't touch my shebang
class BSCommand (build_scripts):
    def run(self):
        """
        Copy, chmod each script listed in 'self.scripts'
        essentially this is the stripped 
         distutils.command.build_scripts.copy_scripts()
        routine
        """
        from stat import ST_MODE
        from distutils.dep_util import newer
        from distutils import log
        import os

        self.mkpath(self.build_dir)
        outfiles = []
        for script in self.scripts:
            outfile = os.path.join(self.build_dir, os.path.basename(script))
            outfiles.append(outfile)

            if not self.force and not newer(script, outfile):
                log.debug("not copying %s (up-to-date)", script)
                continue

            log.info("copying and NOT adjusting %s -> %s", script,
                         self.build_dir)
            self.copy_file(script, outfile)

        if os.name == 'posix':
            for file in outfiles:
                if self.dry_run:
                    log.info("changing mode of %s", file)
                else:
                    oldmode = os.stat(file)[ST_MODE] & 0o7777
                    newmode = (oldmode | 0o555) & 0o7777
                    if newmode != oldmode:
                        log.info("changing mode of %s from %o to %o",
                                 file, oldmode, newmode)
                        os.chmod(file, newmode)

setup(name="name",
      version=version_string,
      description="desc",
      ...
      test_suite='testing',
      cmdclass={'build_scripts': BSCommand},
      )

.. ede/duply.net

Tom de Geus
  • 5,625
  • 2
  • 33
  • 77
ede-duply.net
  • 518
  • 2
  • 5