Home > database >  How to deal with system Python and `brew link python` on MacOS?
How to deal with system Python and `brew link python` on MacOS?

Time:12-22

I cannot link Python 3.8 over system Python 3.9.

I have few installed python versions (3.8, 3.10, etc) by brew and system Python 3.9.

P.S. I cannot uninstall system one (it does not appear in Applications).

$ python3 --version                                
Python 3.10.9

$ brew unlink python@$(python3 --version | grep -oE '[0-9] \.[0-9] '); brew link [email protected]
Unlinking /opt/homebrew/Cellar/[email protected]/3.10.9... 25 symlinks removed.
Warning: Already linked: /opt/homebrew/Cellar/[email protected]/3.8.16
To relink, run:
  brew unlink [email protected] && brew link [email protected]

$ python3 --version                                                                        
Python 3.9.6

$ type python3                                               
python3 is /usr/bin/python3

 $ python3[press TAB]
python3            python3-config     python3.10         python3.10-config  python3.11         python3.11-config

$ python3.8 --version
zsh: command not found: python3.8

 $ echo $PATH
/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin

Questions:

  1. How to set brewed Python over system one?
  2. How to uninstall system python? (not recommended)
  3. How to link python in addition to python3?
  4. Why there is no python3.8 available? (solved with brew reinstall [email protected])

UPD:

  1. python3.8 became available after brew reinstall [email protected].

CodePudding user response:

  1. How to set brewed Python over system one?

All you need to do is prepend /opt/homebrew/bin to the beginning of your path. Your $PATH variable seems to be correctly set, so if it is not working I'd try restarting your terminal.

If all else fails, you can also explicitly prepend /opt/homebrew/opt/python@VERION_NUMBER/bin to the beginning of your path.

  1. How to link python in addition to python3?

If you run brew info python, Homebrew tells you this caveat:

Unversioned symlinks python, python-config, pip etc. pointing to python3, python3-config, pip3 etc., respectively, have been installed into /opt/brew/opt/[email protected]/libexec/bin

So, all you need to do is prepend /opt/brew/opt/python@VERSION_NUMBER/libexec/bin to the beginning of your path. Just add this line to the end of your zshrc, bashrc, or equivalent:

export PATH="/opt/brew/opt/python@VERSION_NUMBER/libexec/bin:$PATH"

This will make all the unversioned Python utils available to you.

CodePudding user response:

  1. It is best to avoid directly overriding (modifying) symbolic or hard links pointing to the system's python version. These might be used by system functions/scripts which are (hopefully) tested against that specific python binary. Changing the version (especially downgrading, but sometimes even upgrading) can break things for the OS.
  2. For the same reason stated above, uninstalling/deleting the system's python should also be avoided.
  3. I struggled myself with MacOS not having python as an executable, so I'll focus my answer on that.
  4. Looks like you already found a way to resolve that.

I can think of 4 ways to get a python executable;

Method #1 - probably not what you want

The easy way to make python available in (z)shell on mac is to add an alias:

echo "alias python=python3" >> ~/.zshrc

With this, in all new zsh sessions (or where .zshrc is reloaded), running python will run the python3 executable in your PATH. You can also set the alias's target to be a full path, so point it to the python version you like (if, for example, one is installed using homebrew). There are two problems: changing the target requires reloading .zshrc, and this will only take effect when executing python from an open shell (will not work for executable scripts/entrypoints - more on that later).

Method #2 - optimal for homebrew

Unversioned python, pip and other such symlinks are installed in libexec/bin directories of brew packages. If you are using homebrew you can add these to your path, like so:

echo 'export PATH="$PATH:/opt/homebrew/opt/python@3/libexec/bin"' >> .zprofile

As homebrew uses symlinks for package versioning, you can replace python@3 in the above example with a more explicit version (e.g. [email protected] or [email protected]).

Method #3 - requires admin privileges

If you have admin privileges, you can add a symlink in your path to the executable, like so (change /usr/bin/python3 to the brew's python executable you like):

sudo ln -s /usr/bin/python3 /usr/local/bin/python

While hardlinks will work as well, I recommend avoiding them as there might be a chance that it will negatively affect minor version upgrades by keeping the older executable (haven't tested myself).
The upside of this method is that it will probably work for all users and all applications, as it is extremely rare for applications to not include /usr/local/bin in the execution search path.

Method #4 - intermediary executable

This (much like method #2) probably won't work for applications that are not invoked by (z)shell (possibly your favorite IDE), but it is most flexible, and does not require admin privileges. It entails adding an intermediary binary, and adding that to your PATH. I usually create a bin directory in my homedir for such occasions, so I'll use that path in the examples. First, create the executable:

mkdir ~/bin
echo '#!/bin/sh\nexec /usr/bin/python3 "$@"' > ~/bin/python
chmod  x ~/bin/python 

Now add ~/bin to your shell's path:

echo 'export PATH="$PATH:$HOME/bin"' >> .zprofile

Alternatively, .zshrc or .zshenv can be used, but additional precaution has to be taken to avoid repeatedly adding ~/bin to the path in subshells:

echo '_home_bindir="$HOME/bin"\n[ "${PATH##*:}" = "$_home_bindir" ] || export PATH="$PATH:$_home_bindir"' >> ~/.zshrc

Now, every new shell (or if using .zshrc - if it is reloaded) will have python executable in its path, and you can easily edit the file to point to your favorite python version.


Notes:

Method #2 is probably the best option if using homebrew, which is the case in question. Still, as I struggled with not having a python executable on a "clean" MacOS installation (without homebrew), I've added methods #1, #3, and #4, in case I'm not the only one not using homebrew's python.

Methods #2, #3, and #4, allow for executable scripts/entrypoints. By that, I'm referring to executable scripts with shebang, most commonly using the env executable:

#!/usr/bin/env python

These 3 methods will also honor additional python arguments (e.g. python -u).

As MacOS no longer comes with a python executable (only python3), I think that method #3 is fairly safe to use (at least from a perspective of not breaking OS scripts). As for $PATH manipulation, I wouldn't recommend prepending it with user-writeable paths (like ~/bin), as this may lead to a security issue, or worse - a debugging nightmare (of course, no one would ever create executables like ~/bin/ls or ~/bin/which, right?).

As for the specific shell, this will also work for bash by changing the relevant files (.z.. to .bash..) as long as the profile/rc files are properly loaded. For other shells (e.g. tcsh) the syntax needs to be adjusted.

  • Related