Friday, August 13, 2021

Brett Cannon: Introducing the Python Launcher for Unix

The problem

Let&aposs say you have more than one version of Python installed on your machine. What version does python3 point to? If you said, "the newest version", you may actually be wrong. That&aposs because python3 points at the last version of Python you installed, not necessarily the newest; python3 typically gets overwritten every time you install some version of Python.

This is a bit annoying when you simply want to work with the newest version of Python (e.g., creating a new virtual environment). It would be convenient if there was a command which just always used the newest version of Python that&aposs installed ...

The solution

It turns out the Python installation of Windows came up with a solution to this annoyance over a decade ago: the Python Launcher for Windows. Through a command named py, you can get the newest version of Python and a convenient listing of all of the known installs of Python on your Windows machine (it&aposs also the default way to launch Python from a python.org install on Windows when python3 is left off of PATH, although that&aposs not the case for the latest PSF Windows Store installs). Being a Windows user at work, I came to find the command rather convenient to have and missed it anytime I did work under macOS or Linux. That felt weird and I decided to do something about that problem.

And so over 3 years ago I set out to re-implement the Python Launcher for Unix in Rust. On July 24, 2021, I launched 1.0.0 of the Python Launcher for Unix (thanks to Kushal Das and Dusty Phillips for double-checking my Rust code). This gives you a py command on Unix which will always use the newest version of Python. What this means is if you run:

py -m venv .venv

you will end up with a virtual environment using the newest version of Python that can be found on $PATH. For this one use alone the command is surprisingly useful. It&aposs also great when you just want a REPL for the newest version of Python that you  have installed.

You can also set the $PY_PYTHON environment variable to specify the version of Python you want to use by default. For instance, let&aposs say you installed CPython 3.10.0.rc1, but you want to use Python 3.9 for your day-to-day. You can set PY_PYTHON = 3.9 and that would cause py to use python3.9.

You can use py --list to see what Python versions you have installed where.

 3.9 │ /usr/local/bin/python3.9                
 3.8 │ /usr/local/opt/python@3.8/bin/python3.8 
 3.7 │ /usr/local/opt/python@3.7/bin/python3.7 
 2.7 │ /usr/bin/python2.7 

All of this mirrors what the Python Launcher for Windows does (although the format of the list is different). There are some other things which are also implemented which are not quite as useful these days since Python 2 is behind us (i.e. you can specify restrictions like what major version of Python you want as well as the major.minor version), but if you are coming from Windows then you have  most of what you&aposre used to from the py command (i.e. configuration files are not implemented and I didn&apost bother with -0 as an alias for --list).

Bonus features

For my workflow

Since I was writing this tool for convenience, I decided to push things out a bit and add some niceties to fit my personal workflow a bit better. 😁

For one thing, if you have a virtual environment in a .venv directory either in the current or any parent directory, it will be used instead of the newest version of Python that&aposs installed. What this means is you don&apost need to activate your virtual environment anymore (although the Launcher will use any activated virtual environment as well)!

py -m venv .venv
py -c "import sys; print(sys.executable)"

I can then continue to use -m to use tools installed into the virtual environment, e.g. py -m pip without issue as well.

This has a surprising benefit for Starship users like myself. By setting the Python binary used to get the version of Python to py, Starship will tell me what version of Python will be used when I run the Python Launcher. Add in folder detection for .venv and now I have Starship tell me what version the virtual environment is for a directory. The FAQ entry on this topic covers how to update your starship.toml to make this all work.

I&aposm also a fish shell user, so I created tab completions for it. The neat thing about them it they are dynamic, so they will be accurate to your machine.

For others&apos workflows

I have also managed to do some things that help others out based on their workflow.

As my first serious CLI tool that I created, I decided to create a man page (from a Markdown file thanks to pandoc). I am also taking the changelog a bit more seriously than I usually do, so thanks to scriv and Ned Batchelder for making some changes to the tool to let me automate nearly my entire release process.

Thanks to Trey Hunner, Jeff Triplett, Ben Spaulding, Carol Willing, and Lorena Mesa being beta testers who happened to be pyenv users, as they contributed a FAQ entry on how to set it up such that the $PY_PYTHON environment variable gets set to what Python version pyenv is set to use.

I also happen to always be on the verge of giving Nushell a serious try, so I went ahead can came up with a one-liner which creates a table of your Python versions.

What the Launcher does ... in picture form

To help understand the overall flow that the Python Launcher for Unix takes in choosing which Python interpreter to use, I created a flowchart using Graphviz.

alt

How to install

If you&aposre interested in installing the Python Launcher for Unix, there are a bunch of options which are all covered in the README. I managed to get a formula into Homebrew/Linuxbrew. Bernát Gábor was nice enough to create an Arch AUR package. I have pre-built binaries for:

  1. RISC-V Linux
  2. AArch64 Linux
  3. x86-64 Linux
  4. Apple Silicon
  5. x86-64 macOS
  6. x86-64 NetBSD

That&aposs basically every platform that I could easily get going under GitHub Actions or build directly on my macOS machine. And if you have cargo installed from Rust, cargo install python-launcher also works (although you do get the bonus features like the man page and fish tab completions).

What the Launcher doesn&apost do

One key thing to point out is the Python Launcher for Unix is not meant to be used in every situation where you may want to launch Python. For instance, if you know the path to the Python you want to use then just launch it directly. Same goes for if you have very specific requirements around what Python interpreter to use (e.g. Framework build on macOS or various bitness builds). The Launcher is purely a convenience and not meant to be The Launcher For All Things; this should never end up in a Docker container.

Otherwise I hope some of you find it useful!



from Planet Python
via read more

No comments:

Post a Comment

TestDriven.io: Working with Static and Media Files in Django

This article looks at how to work with static and media files in a Django project, locally and in production. from Planet Python via read...