37

I'm trying to make changes to an existing python module, and then test it locally. What's the best way to do this?

I cloned the github module and made changes, but I'm not sure how to import the local package instead of the already installed one.

NoDataDumpNoContribution
  • 10,591
  • 9
  • 64
  • 104
Alex Xu
  • 371
  • 1
  • 3
  • 3
  • Are you asking how to import your copy while experimenting with/testing your copy, or how to embed it as "vendor code" into your own app so that it uses the embedded copy instead of whatever's installed, or how to install it into your virtualenv/user/system site-packages over the public released version, or…? – abarnert Sep 09 '18 at 20:45
  • If you run the `python` command from where to local module source is, that will be loaded first before the global version. You can also create a virtual environment. – Burhan Khalid Sep 09 '18 at 20:45
  • Use a [virtual environment](https://docs.python-guide.org/dev/virtualenvs/) – juanpa.arrivillaga Sep 09 '18 at 21:02
  • or you can open a new branch, modify it, then checkout this branch when you want to use it. – Osman Mamun Sep 09 '18 at 21:04
  • See also https://stackoverflow.com/questions/15031694/installing-python-packages-from-local-file-system-folder-to-virtualenv-with-pip – NoDataDumpNoContribution Sep 09 '18 at 21:18
  • And https://setuptools.readthedocs.io/en/latest/setuptools.html#development-mode – NoDataDumpNoContribution Sep 09 '18 at 21:24

5 Answers5

38

The easiest way to do such testing would be to create a virtual environment, and then installing the package in development mode.

Assuming you are on Linux it would look something like this.

$ virtualenv dev_env
$ source dev_env/bin/activate
$ cd ~/project_folder
$ pip install -e .

This workflow would not overwrite the already installed package on your system. Another maybe simpler alternatives would to just use an IDE that handles most of this for you, e.g. PyCharm.

eandersson
  • 25,781
  • 8
  • 89
  • 110
11

TL; DR

You can:

  • Overwrite a module/package by creating another one with the same name in the same folder as the script you'll run.
  • Use the develop mode.

I recommend reading this article that explains pretty well modules and packages.


Overwriting the module/package

Description

You need to create a module or a package (it doesn't make difference) using the same name as the module/package you want and put it in the same folder as the script that it's going to use it.

This because modules are searched starting from the sys.path variable (where the first element is the script's directory)

Example

  1. Create a script with the following contents:
import platform

print(platform.system())
  1. Launching it (python your_test_script.py) should return:

    Output BEFORE

  2. Now in the same directory of the previous test script create a file named exactly platform.py with the following contents:

def system():
    """Just a docstring passing by"""
    return "We have just overwritten default 'platform' module...\nFeel the force!"
  1. If you launch the script now, you'll notice the output is different:

    Output AFTER


Develop mode

Description

Better option if your project is more complicated.

From the root of your package (where you'd launch the build):

pip install -e ./ 

Now you're able to edit code and see the changes in real time..


From The Joy of Packaging:

It puts a link (actually *.pth files) into the python installation to your code, so that your package is installed, but any changes will immediately take effect.

This way all your test code, and client code, etc, can all import your package the usual way.

No sys.path hacking

Community
  • 1
  • 1
LukeSavefrogs
  • 528
  • 6
  • 15
5

One way consists in using sys.path().
For example:

import sys
sys.path.insert(0, path/to/module)

In this way, you give priority to a specific path when looking for a module.
This means that the module you want to import will be searched first in path/to/module and after in the other directories already in sys.path.

The advantage of this approach is that this new order will hold only inside your script without changing the import order of the other ones.

Note: For development purposes you should use a virtualenv as suggested by @eandersson.

abc
  • 11,579
  • 2
  • 26
  • 51
  • is the path just the path on the file system? "~/project/module" I keep getting error ModuleNotFound – Alex Xu Sep 09 '18 at 22:04
  • It depends on its structure. Typically you would have a folder with an `__init__.py` file in it. with other scripts/folders. – abc Sep 09 '18 at 22:10
5

You should probably be doing most of your development work in a virtual environment. Your workflow for this could look like:

# activate the virtual environment in ~/vpy
. $HOME/vpy/bin/activate

# install my app and its dependencies
cd $HOME/src/myapp
pip install -e .

# use my forked library instead
cd $HOME/src/forkedlib
pip install -e .
pytest # or whatever tests the forked lib has

# try it out with my application too
cd $HOME/src/myapp
pytest # or whatever tests your app has
myapp

pip install -e does some magic so that, whenever you import the module in the library, it gets routed directly to the checked-out source tree, so if you make edits in forkedlib and then re-run myapp, you'll see those changes directly.

When you're done, you can pip uninstall forkedlib and then re-run pip install -e . to reinstall your application's (declared) dependencies. (Or delete and recreate the virtual environment, if that's easier.)

David Maze
  • 130,717
  • 29
  • 175
  • 215
  • Thanks for this answer. The final paragraph has me confused. Are you able to embed those examples in the code block? When you say "pip install -e ." to reinstall your application's (declared) dependencies", I thought it would be appropriate to do a normal pip install, not a -e install to return to normal configuration – MattG Nov 28 '22 at 03:10
1

The approach of the answer by abc adding the module path to the system path is fine for local, instant testing, but it's not the full solution, for example when C code needs to be compiled or command line hooks must be set. For a full test you might want to install the package instead.

A typical Python has a setup.py and can be packaged into a distribution file (wheel, ...) which can then be installed locally from a local file.

The workflow would be:

  • create a package distributable (may include a command like python setup.py bdist_wheel)
  • create a new virtual environment for testing (or de-install any previously installed, non-modified version of the package)
  • installe the package from the locally created distributable (may be as simple as pip install --no-index --find-links=..)
  • run the tests

This results in exactly the same situation every future user of the package will find itself in and is a complete test (including the installing process), but it's also a lot of effort, that's why I usually only us the system path method during development, but the full installation way only directly before a release.

NoDataDumpNoContribution
  • 10,591
  • 9
  • 64
  • 104