157

I have a folder for my client code, a folder for my server code, and a folder for code that is shared between them

Proj/
    Client/
        Client.py
    Server/
        Server.py
    Common/
        __init__.py
        Common.py

How do I import Common.py from Server.py and Client.py?

dreftymac
  • 31,404
  • 26
  • 119
  • 182
Drew
  • 12,578
  • 11
  • 58
  • 98

7 Answers7

214

EDIT Nov 2014 (3 years later):

Python 2.6 and 3.x supports proper relative imports, where you can avoid doing anything hacky. With this method, you know you are getting a relative import rather than an absolute import. The '..' means, go to the directory above me:

from ..Common import Common

As a caveat, this will only work if you run your python as a module, from outside of the package. For example:

python -m Proj

Original hacky way

This method is still commonly used in some situations, where you aren't actually ever 'installing' your package. For example, it's popular with Django users.

You can add Common/ to your sys.path (the list of paths python looks at to import things):

import sys, os
sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'Common'))
import Common

os.path.dirname(__file__) just gives you the directory that your current python file is in, and then we navigate to 'Common/' the directory and import 'Common' the module.

Dave
  • 11,499
  • 5
  • 34
  • 46
  • 5
    Do not modify python modules path manually, may be only for quick hacks. Learning Python package management using distutils, setuptools etc. is usually a required skill that will solve problems like that. – Sascha Gottfried Nov 24 '14 at 19:51
  • 1
    @SaschaGottfried totally agree, although if you're not making a distributable package, it probably won't matter. For example, in Django you never really install your app with distutils, so the above method is an easy hack. But anyways I've edited the answer with what I would do these days. – Dave Nov 27 '14 at 14:06
  • 59
    Thanks for answering the actual question instead of preaching about proper technique. There are plenty of good reasons to do relative imports. – shrewmouse Oct 29 '15 at 18:09
  • how would you go up more than one level? – jxramos Apr 12 '18 at 19:51
  • In my case, a third-party GUI testing application uses python scripts for implementing steps, and the scripts are imported such that I can't use proper relative imports. There is no project-level variable for the top directory for me to use absolute paths in the python, and it needs to run in a CI environment where hardcoded absolute paths are the devil. – John Neuhaus Jul 30 '18 at 19:37
  • 21
    to go up one more level, use an additional dot for each level. @jxramos ex: `from ...myfile` goes to `../../myfile` – WattsInABox Aug 21 '18 at 19:56
  • 6
    @WattsInABox how would you go up and go to a file in a different directory, say the equivalent of `../../mydir2/myfile`? – Aaron John Sabu Feb 05 '21 at 17:51
12

Funny enough, a same problem I just met, and I get this work in following way:

combining with linux command ln , we can make thing a lot simper:

1. cd Proj/Client
2. ln -s ../Common ./

3. cd Proj/Server
4. ln -s ../Common ./

And, now if you want to import some_stuff from file: Proj/Common/Common.py into your file: Proj/Client/Client.py, just like this:

# in Proj/Client/Client.py
from Common.Common import some_stuff

And, the same applies to Proj/Server, Also works for setup.py process, a same question discussed here, hope it helps !

Community
  • 1
  • 1
jacoolee
  • 153
  • 1
  • 3
11

Doing a relative import is absolulutely OK! Here's what little 'ol me does:

#first change the cwd to the script path
scriptPath = os.path.realpath(os.path.dirname(sys.argv[0]))
os.chdir(scriptPath)

#append the relative location you want to import from
sys.path.append("../common")

#import your module stored in '../common'
import common.py
Gary Beardsley
  • 127
  • 1
  • 2
  • 2
    But you better know where sys.argv[0] is actually pointing - it (prolly) isn't the directory you were in when you started python. – CarlH Jul 14 '14 at 16:51
  • This is a quick hack, with lot of pitfalls. But the question was not even better. – Sascha Gottfried Nov 24 '14 at 19:52
  • 1
    This clearly written, but the original hack in [Dave's answer](https://stackoverflow.com/a/7506029/3499424) is better because it uses `__file__` to get the proper relation from the current file – John Neuhaus Jul 30 '18 at 19:39
11

Don't do relative import.

From PEP8:

Relative imports for intra-package imports are highly discouraged.

Put all your code into one super package (i.e. "myapp") and use subpackages for client, server and common code.

Update: "Python 2.6 and 3.x supports proper relative imports (...)". See Dave's answers for more details.

Michał Šrajer
  • 30,364
  • 7
  • 62
  • 85
  • 1
    Imagine that you add some code to the end of the client and server after the '`if __name__ == "__main__":`' line. That is, you want to be able to use them as stand-alone scripts. How to do it properly? I think it's a perfectly common use case that should be supported. Why is it discouraged? – Jabba Jan 01 '14 at 22:31
  • 99
    I am surprised that "Don't do it" is the accepted answer for a "how do I..." question (well, except for Rails .) There **are** occasional reasons to do this. I use a solution similar to what Dave suggests. – Tom Wilson Feb 24 '14 at 18:33
  • 1
    @TomWilson: It's not pure "don't do it" answer. There is "do it this way" below. – Michał Šrajer Dec 01 '14 at 09:49
  • 3
    Someone should tell the guys over at Numpy! They use a TON of relative imports! – Austin A Jul 13 '15 at 16:48
  • 18
    This answer is not applicable to current versions of Python. The quoted part is no longer to be found in PEP 8. Nowadays it reads like: _"explicit relative imports are an acceptable alternative to absolute imports, especially when dealing with complex package layouts where using absolute imports would be unnecessarily verbose"_ – moooeeeep Jan 04 '17 at 12:11
  • it would clearly be a poor design choice, but it would be useful for example in code refactoring when I want to access the same routines from different places and compare different versions without needs of moving or copying files. – Vincenzooo Jul 01 '21 at 08:12
  • I came from a Javascript/nodejs background and I'm really surprised as such thing doesn't have standard way, I've tried multiple solutions, still bugged with that `__init__.py` file solution, as it doesn't always work. it works normally with the `pytest`, but not with the actual `python3 file` run command. – mr.xed Dec 26 '22 at 14:40
6

The default import method is already "relative", from the PYTHONPATH. The PYTHONPATH is by default, to some system libraries along with the folder of the original source file. If you run with -m to run a module, the current directory gets added to the PYTHONPATH. So if the entry point of your program is inside of Proj, then using import Common.Common should work inside both Server.py and Client.py.

Don't do a relative import. It won't work how you want it to.

Jonathan Sternberg
  • 6,421
  • 7
  • 39
  • 58
3

A simple answer for novice in Python world

Create a simple example

Assume we run ls -R in the current working directory and this is the result:

./second_karma:
enemy.py  import.py  __init__.py  math

./second_karma/math:
fibonacci.py  __init__.py

And we run this command $ python3 second-karma/import.py

init.py is an empty file but it should exists.

Now let's see what is inside the second-karma/import.py:

from .math.fibonacci import Fibonacci
fib = Fibonacci()
print(fib.get_fibonacci(15))

And what is inside the second_karma/math/fibonacci.py:

from ..enemy import Enemy
class Fibonacci:
    enemy: Enemy

    def __init__(self):
        self.enemy = Enemy(150,900)
        print("Class instantiated")
    
    def get_fibonacci(self, which_index: int) -> int:
        print(self.enemy.get_hp())
        return 4

Now the last file is second_karma/enemy.py:

class Enemy:
    hp: int = 100
    attack_low: int = 180
    attack_high: int = 360

    def __init__(
            self, 
            attack_low: int,
            attack_high: int) -> None: 
        self.attack_low = attack_low
        self.attack_high = attack_high

    def getAttackPower(
            self) -> {"attack_low": int, "attack_high": int}:
        return {
            "attack_low": self.attack_low,
            "attack_high": self.attack_high
        }

    def get_hp(self) -> int:
        return self.hp

Now a simple answer why it was not working:

  • Python has a concept of packages, which is basically a folder containing one or more modules, and zero-or-more packages.
  • When we launch python, there are two ways of doing it:
    • Asking python to execute a specific module (python3 path/to/file.py).
    • Asking python to execute a package.
  • The issue is that import.py makes reference to importing .math
    • The .math in this context means "go find a module/package in the current package with the name math"
    • Trouble:
      • When I execute as $ python3 second-karma/import.py I am executing a module, not a package. thus python has no idea what . means in this context
      • Fix:
        python3 -m second_karma.import
        
      • Now import.py is of parent package second_karma, and thus your relative import will work.

Important note:

Those __init__.py are necessary and if you have not them you must create them first.

An example in github

Kasir Barati
  • 606
  • 13
  • 24
1

Approch used by me is similar to Gary Beardsley mentioned above with small change.

Filename: Server.py

import os, sys
script_path = os.path.realpath(os.path.dirname(__name__))
os.chdir(script_path)
sys.path.append("..")
# above mentioned steps will make 1 level up module available for import
# here Client, Server and Common all 3 can be imported.

# below mentioned import will be relative to root project
from Common import Common
from Client import Client