Python Setup#

We want a nice home for our Python project: metadata, dependencies, and sharing. We’ll choose the really-modern Python approach:

  • pyproject.toml

  • setuptools as the build backend (no Poetry/Hatch, just regular pip)

  • An editable install


Python certainly has…lots of options. Why this as-yet-little-known approach?

Why an “editable install”? The Python testing community encourages an src layout of your project, as recommended by others. When you make your workspace into a proper “package”, you can import from anywhere…once the package is an “editable install”.

Why not just a requirements.txt? This won’t get you into “proper package” mode. Ditto for pipenv which is more about applications than packages.

Why not The Python world is (hopefully) moving away from that, to a world with pyproject.toml as the central configuration spot.

Why not Poetry or Hatch? This series is trying to stay on a mainstream path. pip is still the king of the hill. Now that setuptools is a valid pyproject.toml backend, that’s a good happy path for beginners. Also, setuptools directly supports the src layout described above.

Dear heavens, I hope one day to never need to explain that again.

Python editable install with pyproject.toml#

We’re doing a “joyful Python” project. That means coding through the lens of a test. pytest best practices say to make a package and then do an “editable install.”

We’ll follow the setuptools page above, starting with an empty pyproject.toml.

$ touch pyproject.toml

Build backend#

In the first section of the TOML file, we need to tell our packaging tool what build backend to use. There are lots of packaging tools – we’re using pip. There are a number of backends – we’re using setuptools:

requires = ["setuptools"]
build-backend = "setuptools.build_meta"

Project metadata#

Next, we’ll tell our tooling – and the world – a little about our project. Add this section to the pyproject.toml file:

name = "pyodide_components"
version = "0.0.1"
requires-python = ">=3.10"
license = {text = "BSD 3-Clause License"}
classifiers = [
    "Programming Language :: Python :: 3",
dependencies = [

We’re doing the minimum for dependencies for now: just Sphinx, as a way to ensure our installation works.

Project directory#

Let’s put some empty code into our project directory. We said we were adopting the src layout. Make an empty package file as a starter:

$ touch src/pyodide_components/```

Virtual environment#

We want to follow best practices and work in a virtual environment. Make one in the project folder, then upgrade the pip and setuptools it uses:

$ python -m venv .venv
$ .venv/bin/pip install --upgrade pip setuptools

Editable install#

We have a virtual environment. We have a pyproject.toml that defines our package. But the virtual environment needs to know about our package.

Let’s do an editable install. This put pyodide_components in the virtual environment’s site-packages. But, as basically a symbolic link back to the src/pyodide_components directory:

$ .venv/bin/pip install -e .

If this worked correctly, you now have a src/pyodide_components.egg-info directory. You also have sphinx-quickstart in your virtual environment’s bin:

$ ls src/pyodide_components.egg-info 
PKG-INFO              requires.txt
SOURCES.txt             top_level.txt
$ ls .venv/bin/sphinx*

To confirm that we can import pyodide_components outside its source directory:

$ .venv/bin/python -c "import pyodide_components"


There we go, a modern Python workspace. Let’s clean up a bit.

First, add some exclusions to your .gitignore file:


Now commit your work:

$ git add .gitignore pyproject.toml src
$ git commit -m"Python project workspace setup"