1

I would like to install python3.7 and idle-python3.7 on my stable Debian box, but they are from sid and require newer versions of libc, which would require upgrading loads and loads of core packages to unstable versions if I do it the normal way. Plus, it would involve upgrading or removing lots of packages that, for whatever reason, specify exactly python3.5 as one of their dependencies. I don't want to do that.

I also can't compile python3.7 for some reason; when I try, the tests fail. The Debian maintainers managed to compile it for my architecture, though, so I want to use their version.

How can I install these conflicting packages?

1

They are both available on Buster (Testing) https://packages.debian.org/search?keywords=python3.7

You should be able to backport them from Testing into Stable https://backports.debian.org/Instructions/

| improve this answer | |
  • I shall be frustrated if this works. Won't this install on top of the existing python3? – wizzwizz4 Jul 25 '18 at 13:47
1

Warning: This is a hack. As such, I make no promises as to its reliability. It is less likely to trash your system than the alternative, though.


In order to do this, we will be installing a new copy of Debian inside your file system, chrooting into it to install some packages, then creating a sort of hybrid system inside a separate namespace to run the program. Root access is required for some of the steps, and should be optional for others (but I haven't worked that out yet). You'll need a shell, debootstrap, chroot and gcc (the latter because we need a custom tool).

Installing a minimal Debian instance

In order to do this, we'll need a program called debootstrap. This program, as the name suggests, lets you bootstrap a Debian installation. You can install it with:

sudo apt-get install debootstrap

Now that that's done, we can get to the actual installing.

  1. First, check which version of Debian your desired packages are in.
    Mine are in sid at time of writing.
  2. Now determine where you'd like to store this Debian image.
    I am boring, and expect to need to do this more than once, so I called mine chroot.0 and put it straight into my CWD (which is also ~).
  3. Run this command:

    sudo debootstrap --variant=minbase sid chroot.0
    

    replacing sid with the desired version of Debian and chroot.0 with the path to the directory you'd like to use.

    You might also be able to use use --variant=fakeroot and potentially avoid using sudo here, but that will install lots of extra packages (unless you can combine --variants) and might invalidate the rest of the answer due to permissions problems.

While this is running, you can be reading the next section of this answer.

Installing the packages

This is the easy part. Once the system's installed, you can chroot into it to install the packages you want.

  1. chroot into the minimal Debian instance:

    sudo chroot chroot.0
    

    (again replacing chroot.0 with the path you used).

  2. You should now be in a root shell, where you can install the packages you want:

    apt install idle-python3.7 python3.7
    
  3. Leave the chroot by running exit.

Now, we could just run the packages from the chroot. But then they'd be running as root and isolated from your home directory, unless you put in the effort of creating a new user inside the chroot and setting up a bound mount point to your home directory... and at that point it starts to be like setting up a whole new system. Instead, let's start creating tools.

Create a tool

Save this somewhere:

#include <stdio.h>
#include <string.h>
#include <sys/mount.h>
#include <linux/limits.h>

int main(int argc, char **argv) {
    if (argc < 3) {
        printf(
"This is a single-use, disposable program that binds specified directories from\n"
"a chroot's root to the current root.\n"
"\n"
"Argument #1 should be the path to the chroot directory, sans trailing /.\n"
"Subsequent arguments should be paths relative to the root, with preceeding /.\n"
        );
        return 2;
    }
    for (int i = 2; i < argc; i += 1) {
        char source[PATH_MAX];  /* Not really MAX, but quite big. */
        strcpy(&source, argv[1]);
        strcat(&source, argv[i]);
        if (mount(&source, argv[i],
                  NULL, MS_BIND,
                  NULL) == -1) {
            return 1;
        }
    }
    return 0;
}

and compile it with gcc:

gcc bindfromchroot.c -o bindfromchroot

I called it bindfromchroot because that's what it does. The reason we can't just use mount --bind is that at least 2 calls to mount are needed; one of those will shadow mount and another will shadow its libraries - both of which will stop mount running a subsequent time and leave us with a broken namespace which we'll have to exit from.

Now that all of our setup is done, we can run some commands that should probably be put into a shell script.

Create the namespace and run the program

  1. Run sudo unshare -m su YOUR_USERNAME or sudo unshare -m su $(id -un) to launch a shell in a separate namespace as you (or the current user). That "separate namespace" is important; it means that when we mount parts of our minimal Debian instance over the host Debian, the changes will only apply to our new shell and its child processes (as opposed to every process). It might be a good idea to append -c /bin/sh to the command, because bash displays annoying warning messages about stuff going catastrophically wrong after we've run the next step.
  2. Run the tool you compiled earlier. It requires sudo, and doesn't really tell you about errors save via echo $?, but it gets the job done. How much of Debian you need to bind over depends on the program, but I strongly suggest being as specific as possible and not binding over /etc, /dev or /boot. To get Python 3.7 to work on my machine, I ran:

    sudo ./bindfromchroot chroot.0 /usr /lib
    
  3. Test the program you want to run, e.g. idle-python3.7. If it doesn't work, check the error messages, work out what it's missing, exit and try again from step 1.

Writing a shell script to run your program automatically

I've managed a one-liner for this. Modify as appropriate:

sudo unshare -m su wizzwizz4 -c sh -c 'sudo -S ./bindfromchroot chroot.0 /usr /lib; idle-python3.7'

If you wish this to preserve environment variables, use this version instead:

sudo -E unshare -m su wizzwizz4 -mc sh -c 'sudo -ES ./bindfromchroot chroot.0 /usr /lib; idle-python3.7'

You can change the sudo -ES to sudo -EA to run the program defined in the SUDO_ASKPASS environment variable, which may be a graphical prompt (such as the one provided with git gui) if you want.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.