What is the proper way to work with shared modules in Python development?

PythonPython 3.xDistutils

Python Problem Overview


I'm working toward adopting Python as part of my team's development tool suite. With the other languages/tools we use, we develop many reusable functions and classes that are specific to the work we do. This standardizes the way we do things and saves a lot of wheel re-inventing.

I can't seem to find any examples of how this is usually handled with Python. Right now I have a development folder on a local drive, with multiple project folders below that, and an additional "common" folder containing packages and modules with re-usable classes and functions. These "common" modules are imported by modules within multiple projects.

Development/
    Common/
        Package_a/
        Package_b/
    Project1/
        Package1_1/
        Package1_2/
    Project2/
        Package2_1/
        Package2_2/
        

In trying to learn how to distribute a Python application, it seems that there is an assumption that all referenced packages are below the top-level project folder, not collateral to it. The thought also occurred to me that perhaps the correct approach is to develop common/framework modules in a separate project, and once tested, deploy those to each developer's environment by installing to the site-packages folder. However, that also raises questions re distribution.

Can anyone shed light on this, or point me to a resource that discusses this issue?

Python Solutions


Solution 1 - Python

If you have common code that you want to share across multiple projects, it may be worth thinking about storing this code in a physically separate project, which is then imported as a dependency into your other projects. This is easily achieved if you host your common code project in github or bitbucket, where you can use pip to install it in any other project. This approach not only helps you to easily share common code across multiple projects, but it also helps protect you from inadvertently creating bad dependencies (i.e. those directed from your common code to your non common code).

The link below provides a good introduction to using pip and virtualenv to manage dependencies, definitely worth a read if you and your team are fairly new to working with python as this is a very common toolchain used for just this kind of problem:

http://dabapps.com/blog/introduction-to-pip-and-virtualenv-python/

And the link below shows you how to pull in dependencies from github using pip:

https://stackoverflow.com/questions/7322334/how-to-use-python-pip-install-software-to-pull-packages-from-github

Solution 2 - Python

The must-read-first on this kind of stuff is here:

https://stackoverflow.com/questions/193161/what-is-the-best-project-structure-for-a-python-application

in case you haven't seen it (and follow the link in the second answer).

The key is that each major package be importable as if "." was the top level directory, which means that it will also work correctly when installed in a site-packages. What this implies is that major packages should all be flat within the top directory, as in:

myproject-0.1/
    myproject/
        framework/
    packageA/
        sub_package_in_A/
            module.py
    packageB/
        ...

Then both you (within your other packages) and your users can import as:

import myproject
import packageA.sub_package_in_A.module

etc

Which means you should think hard about @MattAnderson's comment, but if you want it to appear as a separately-distributable package, it needs to be in the top directory.

Note this doesn't stop you (or your users) from doing an:

import packageA.sub_package_in_A as sub_package_in_A

but it does stop you from allowing:

import sub_package_in_A

directly.

Solution 3 - Python

I think that this is the best reference for creating a distributable python package:

link removed as it leads to a hacked site.

also, don't feel that you need to nest everything under a single directory. You can do things like

platform/
    core/
        coremodule
    api/
        apimodule

and then do things like from platform.core import coremodule, etc.

Solution 4 - Python

> ...it seems that there is an assumption that all referenced packages > are below the top-level project folder, not collateral to it.

That's mainly because the current working directory is the first entry in sys.path by default, which makes it very convenient to import modules and packages below that directory.

If you remove it, you can't even import stuff from the current working directory...

$ touch foo.py
$ python
>>> import sys
>>> del sys.path[0]
>>> import foo
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: No module named foo

> The thought also occurred to me that perhaps the correct approach is > to develop common/framework modules in a separate project, and once > tested, deploy those to each developer's environment by installing to > the site-packages folder.

It's not really a major issue for development. If you're using version control, and all developers check out the source tree in the same structure, you can easily employ relative path hacks to ensure the code works correctly without having to mess around with environment variables or symbolic links.

> However, that also raises questions re distribution.

This is where things can get a bit more complicated, but only if you're planning to release libraries independently of the projects which use them, and/or having multiple project installers share the same libraries. It that's the case, take a look at distutils.

If not, you can simply employ the same relative path hacks used in development to ensure you project works "out of the box".

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionSteve SawyerView Question on Stackoverflow
Solution 1 - PythonrobjohncoxView Answer on Stackoverflow
Solution 2 - PythonEthan CoonView Answer on Stackoverflow
Solution 3 - PythonCameron SparrView Answer on Stackoverflow
Solution 4 - PythonAyaView Answer on Stackoverflow