3

I have several python projects (eg. a, b) that were written independently with each other. I would like to create a third project (c) which uses the routines of these two projects without introducing too many changes to the main contents of (a, b).

Minor changes to the import statements inside (a, b) are ok provided that (a.py, b.py) should still work while called in their respective project folders.

The problem is a bit similar to a previous post (Re-importing different python module with same name) but:

  1. I'm using Python 3 so relative imports seems to work differently.

  2. I would like to know how to replace the line "from utils import *" as shown in the example below.

  3. I have another utils.py under the current working directory shadowing the other utils.py in projects a and b.


For example my project is as follows:

  • a
    • a.py
    • utils.py
  • b
    • b.py
    • utils.py
  • c
    • main.py
    • utils.py

Contents of a/a.py:

import utils
utils.hello()

Contents of a/utils.py:

def hello():
    print('hello from a')

Contents of b/b.py:

from utils import *
hello()

Contents of b/utils.py:

def hello():
print('hello from b')

Contents of main.py

import sys
sys.path.append('../a')
import a
sys.path.append('../b')
import b
import utils

utils.hello()

The example prints (I'd like the three lines to print a,b,c respectively):

hello from c
hello from c
hello from c

It looks to me that sys.path.insert and sys.append are not very good practice to import modules and could make the project vulnerable to bugs as the project scales up.

matohak
  • 535
  • 4
  • 19

3 Answers3

3

I'm using Python 3 so relative imports no longer works.

Not sure why you came to that conclusion, but:

In a.py:

from .utils import hello # the . is important!
hello()

(similarly in b.py & c.py)

prints the desired output

Do note that relative imports in python 3 require the from ... import ... syntax


Edit:

If your main.py is in c/, you need to alter the import path, because relative imports cannot go above the working directory where python was started (that's why you get errors if you import a or b in c.

You can either alter the env var PYTHONPATH (see norok's answer) or you can do that in main.py:

import sys
sys.path.append('..')
import b.b # no more errors!

Do note that you still need the relative imports for utils in a.py, b.py and main.py.

GPhilo
  • 18,519
  • 9
  • 63
  • 89
  • This gives me an error `ImportError: attempted relative import with no known parent package` – matohak Sep 26 '19 at 13:57
  • The relative import won't work if typed in the console. Write it in `a.py`, then from the console `import a` – GPhilo Sep 26 '19 at 13:58
  • I put `from .utils import hello` into `a.py` and `b.py` and executed main.py from the terminal while inside the directory `c` – matohak Sep 26 '19 at 13:59
  • Oh I didn't see `main` is in `c/`, I put mine in the root. Let me test this configuration – GPhilo Sep 26 '19 at 14:00
1

I think you should be using the PYTHONPATH variable in order to avoid using sys.

Content of a/a.py and b/b.py:

import utils
utils.hello()

Content of X/utils.py with X one of a, b or c:

def hello():
    print('hello from X')

Content of c/main.py:

import a.a
import b.b

import utils
utils.hello()

Output (when running: export PYTHONPATH='$PYTHONPATH:<project_root>'; python main.py):

hello from a
hello from b
hello from c
norok2
  • 25,683
  • 4
  • 73
  • 99
0

Add an empty file called __init__.py to each of the folders a and b. This marks these folders as packages. https://docs.python.org/3/reference/import.html#regular-packages Then (after adding the folders to path) you should be able to import a as import a.a and if you wanted it, you could import a's version of utils.py as import a.utils.

Simon Crane
  • 2,122
  • 2
  • 10
  • 21
  • 2
    Or, rather, I just tested it and I can confirm the `__init__.py` file is not necessary in python 3 – GPhilo Sep 26 '19 at 13:50