An interactive or editable python package install enables you to see real time changes when developing a package. Instead of uninstalling, repackaging, and reinstalling your package an interactive install immediately reflects your code changes.

Software Version Checks

Just for the record I’m going to check the version of all of the packages I have installed on my system, the packages needed in the build and install process.

[email protected]:~$ python3 -m pip show setuptools | grep Version
Version: 65.3.0
[email protected]:~$ python3 -m pip show build | grep Version
Version: 0.8.0
[email protected]:~$ python3 -m pip show twine | grep Version
Version: 4.0.1
[email protected]:~$ python3 -m pip show pip | grep Version
Version: 22.2.2
[email protected]:~$ 

Let’s also note the Ubuntu version I am running on too.

[email protected]:~$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 22.04.1 LTS
Release:        22.04
Codename:       jammy

I’m doing this check as it seems I have caught Python Packaging with TOML at an interesting time. As of today, 31st August 2022, reading this documentation interactive installs (or editable installs) has just been merged in to main for the “setuptools” build system.

Interactive Install / Editable Install Attempt With Virtual Environment Venv

To follow along you will need a python package, if you don’t have your own please follow this post first to create your first python package using the “setup tools” build system. Come back here when ready.

I’m going to use the package “pkgexample” we created in that other post, and were going to try and install it in editable mode.

[email protected]:~/myrepos/rex$ tree -a pkgexample/

pkgexample/
├── .gitignore
├── LICENCE
├── pyproject.toml
├── README.md
└── src
    └── pkgexample
        ├── helloworld.py
        └── __init__.py

2 directories, 6 files
[email protected]:~/myrepos/rex$

From the root directory we’re going to run the following code.

python3 -m pip install -e .

Let’ see how this turns out for us.

[email protected]:~/myrepos/rex/pkgexample$ python3 -m pip install -e .
Defaulting to user installation because normal site-packages is not writeable
Obtaining file:///home/ubuntu/myrepos/rex/pkgexample
  Installing build dependencies ... done
  Checking if build backend supports build_editable ... done
ERROR: Project file:///home/ubuntu/myrepos/rex/pkgexample has a 'pyproject.toml' and its build backend is missing the 'build_editable' hook. Since it does not have a 'setup.py' nor a 'setup.cfg', it cannot be installed in editable mode. Consider using a build backend that supports PEP 660.
[email protected]:~/myrepos/rex/pkgexample$

Not very well.

ERROR: Project file:///home/ubuntu/myrepos/rex/pkgexample has a ‘pyproject.toml’ and its build backend is missing the ‘build_editable’ hook. Since it does not have a ‘setup.py’ nor a ‘setup.cfg’, it cannot be installed in editable mode. Consider using a build backend that supports PEP 660.

If this is how you remember editable installs the above error message probably surprised you.

Things change – come one, let’s take a look.

The “setuptools” documentation now recommends creating a virtual environment inside your root project directory and then running the interactive install command – lets try their recommended approach.

[email protected]:~/myrepos/rex/pkgexample$ python3 -m venv .venv
[email protected]:~/myrepos/rex/pkgexample$ source .venv/bin/activate
(.venv) [email protected]:~/myrepos/rex/pkgexample$ pip install -e .
Obtaining file:///home/ubuntu/myrepos/rex/pkgexample
  Installing build dependencies ... done
  Checking if build backend supports build_editable ... done
  Getting requirements to build editable ... done
  Installing backend dependencies ... done
  Preparing editable metadata (pyproject.toml) ... done
Building wheels for collected packages: pkgexample
  Building editable for pkgexample (pyproject.toml) ... done
  Created wheel for pkgexample: filename=pkgexample-1.0.1-0.editable-py3-none-any.whl size=3060 sha256=9a01c34f780d08b90f688fa966d16d8c283727022dc7bb4cc63f625346e78f0a
  Stored in directory: /tmp/pip-ephem-wheel-cache-lyd0t8zf/wheels/12/47/38/a45fdd3c712bbe4ff26cfdbab7c85e633230070d7f3f95f9b1
Successfully built pkgexample
Installing collected packages: pkgexample
Successfully installed pkgexample-1.0.1
(.venv) [email protected]:~/myrepos/rex/pkgexample$

I seems to have worked. We have dropped in to a virtual environment ( more on these in another post), and it looks like the package has been installed interactively. Lets test this.

Opening a Python3 shell, and importing our package modules does show it as installed.

(.venv) [email protected]:~/myrepos/rex/pkgexample$ python3
Python 3.10.4 (main, Jun 29 2022, 12:14:53) [GCC 11.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from pkgexample.helloworld import HelloWorld
>>> my_helloworld_object=HelloWorld()
>>> my_helloworld_object.print_message()
Hello World
>>>

Now exiting, exit(), the python shell, I am going to make a change to my helloworld.py module and change the message to “Hello Interactive World” and save the changes.

class HelloWorld:
    def __init__(self):
        self.a_simple_message="Hello Interactive World"

    def print_message(self):
        print(self.a_simple_message)

Opening a python shell and running the same commands should show a different result.

(.venv) [email protected]:~/myrepos/rex/pkgexample$ python3
Python 3.10.4 (main, Jun 29 2022, 12:14:53) [GCC 11.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from pkgexample.helloworld import HelloWorld
>>> my_helloworld_object=HelloWorld()
>>> my_helloworld_object.print_message()
Hello Interactive World
>>>

It does.

Interactive Install With No Virtual Env

Want to stick with the old ways? You might be able to with this work around.

Uninstall Your Package And Virtual Environment

If you want to attempt this workaround but played along with the above recommended venv approach, please apply the following instructions to uninstall the package “pkgexample”, deactivate and delete your virtual environment.

(.venv) [email protected]:~/myrepos/rex/pkgexample$ python3 -m pip uninstall pkgexample
Found existing installation: pkgexample 1.0.1
Uninstalling pkgexample-1.0.1:
  Would remove:
    /home/ubuntu/myrepos/rex/pkgexample/.venv/lib/python3.10/site-packages/__editable__.pkgexample-1.0.1.pth
    /home/ubuntu/myrepos/rex/pkgexample/.venv/lib/python3.10/site-packages/pkgexample-1.0.1.dist-info/*
Proceed (Y/n)? y
  Successfully uninstalled pkgexample-1.0.1
(.venv) [email protected]:~/myrepos/rex/pkgexample$

Then deactivate your virtual environment / venv.

(.venv) [email protected]:~/myrepos/rex/pkgexample$ deactivate
[email protected]:~/myrepos/rex/pkgexample$

Then delete your venv directory.

[email protected]:~/myrepos/rex/pkgexample$ rm -rf .venv/
[email protected]:~/myrepos/rex/pkgexample$

Your project directory should now be restored to its original state, and the package “pkgexample” should be now uninstalled.

The Work Around

This is a little bit hacky, and I don’t think it is recommended procedure.

But here is the process.

If you re-read the error message we saw earlier and focus in on this part, you will see it wants a ‘setup.py’ file.

Since it does not have a ‘setup.py’ nor a ‘setup.cfg’

The current “setuptools” documentation doesn’t cover trying to do what the error message says so I am not sure if they intend this approach to be available longer term. Lets go ahead and do it anyway.

Give the error what it wants and create a new python module at the root of your project, name it ‘setup.py’.

[email protected]:~/myrepos/rex$ tree -a pkgexample/

pkgexample/
├── .gitignore
├── LICENCE
├── pyproject.toml
├── README.md
├── setup.py              <----New File
└── src
    └── pkgexample
        ├── helloworld.py
        └── __init__.py

2 directories, 6 files
[email protected]:~/myrepos/rex$

Add the following contents. Save and exit.

from setuptools import setup

if __name__ == '__main__':
    setup()

Now you would expect running the interactive install command to now work.
Unfortunately it errors out.

[email protected]:~/myrepos/rex/pkgexample$ python3 -m pip install -e .
Defaulting to user installation because normal site-packages is not writeable
Obtaining file:///home/ubuntu/myrepos/rex/pkgexample
  Installing build dependencies ... done
  Checking if build backend supports build_editable ... done
  Getting requirements to build wheel ... done
  Installing backend dependencies ... done
  Preparing metadata (pyproject.toml) ... done
Installing collected packages: UNKNOWN
  Running setup.py develop for UNKNOWN
    error: subprocess-exited-with-error

Apparently this is a bug as described here. It is marked as closed, but clearly it appears not to have been fixed.

Oh well… there is a work around for this bug and that is to use the –no-build-isolation switch. The modified interactive install command is now as follows.

python3 -m pip install --no-build-isolation -e .

More details on the –no-build-isolation switch can be found here.

[email protected]:~/myrepos/rex/pkgexample$ python3 -m pip install --no-build-isolation -e .
Defaulting to user installation because normal site-packages is not writeable
Obtaining file:///home/ubuntu/myrepos/rex/pkgexample
  Checking if build backend supports build_editable ... done
  Preparing editable metadata (pyproject.toml) ... done
Building wheels for collected packages: pkgexample
  Building editable for pkgexample (pyproject.toml) ... done
  Created wheel for pkgexample: filename=pkgexample-1.0.1-0.editable-py3-none-any.whl size=3060 sha256=a65f9d3289ab1a92cb82bb8148b3bd20c1fb8727b682f2f68868a0200e2406a5
  Stored in directory: /tmp/pip-ephem-wheel-cache-vcmukrup/wheels/12/47/38/a45fdd3c712bbe4ff26cfdbab7c85e633230070d7f3f95f9b1
Successfully built pkgexample
Installing collected packages: pkgexample
Successfully installed pkgexample-1.0.1
[email protected]:~/myrepos/rex/pkgexample$

Does this workaround work? Yes it does. Here is the output when changing my message value with this workaround interactive install.

[email protected]:~/myrepos/rex/pkgexample$ python3
Python 3.10.4 (main, Jun 29 2022, 12:14:53) [GCC 11.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from pkgexample.helloworld import HelloWorld
>>> my_helloworld_object=HelloWorld()
>>> my_helloworld_object.print_message()
Hello World
>>> exit()
[email protected]:~/myrepos/rex/pkgexample$ python3
Python 3.10.4 (main, Jun 29 2022, 12:14:53) [GCC 11.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from pkgexample.helloworld import HelloWorld
>>> my_helloworld_object=HelloWorld()
>>> my_helloworld_object.print_message()
Hello Workaround World
>>>

Closing Notes

Well, as you can see, the current state of interactive / editable installs with “pyproject.toml” configuration files and the “src/source layout” package structure is a little rough around the edges.

Not too rough, and does work with some minimal workarounds.
When they get this fixed, it will be awsome.

Stay tuned for further updates in the exciting world of interactive installs.

Let me know in the comments if you find out anything extra.

Further Reading

https://github.com/pypa/setuptools/pull/3488

https://github.com/pypa/setuptools/pull/3488

https://github.com/pypa/setuptools/blob/main/docs/userguide/development_mode.rst


2 thoughts on “Python Package Interactive Install / Editable Install With “pyproject.toml””
%d bloggers like this: