Python's sys.path and the import statement?

www.2shared.com/file/imvicAxG/test.html

Before I begin, I know this is a Python question, but it’s so much easier to demonstrate the problem with Panda’s input and tasks.

Also, I know you might be wondering why do I need to append to sys.path here, but I do in my real app, this is just a demonstration of the problem.

So here we have a folder with a module “main.py”, the first one to run. We also have the modules
“importer.py”
“anotherimporter.py”
“importee.py”

“importer” and “importee” are in the subfolder “src”, while “anotherimporter” in the main folder.

  1. “importer” imports “importee” and “anotherimporter”
  2. “anotherimporter” also imports “importee”
  3. “anotherimporter” imports “importee” without specifying it’s path, as “src” is added to sys.path.

Now “importee” has a variable “state” which you can set to True or False by pressing and releasing the button “a”. A task prints it’s value each frame.

Now, if you specify the real path to “importee” in “anotherimporter” (src.importee), you will get the right boolean values printed to the console, but if you don’t specify the path (like in my example) and let sys.path handle it, you get something like this printed to the console when you press the button “a”

True
False
True
False
True
False
True
False
True
False
True
False

even though you haven’t let go of it.

What’s going on?

Sorry if this was long, it took me a lot to even figure out and try to explain this issue I have.

Hi, when Python imports module it executes their code if they are module which are not loaded yet. Importing modules with your path modifications implies that you will import your module (and execute its code) two times - you can verify that adding:

print __name__

at the beginning of importee.py (or, for example, another print before the creation of the task). You’ll see that that line will be executed two times, each time it has a different name. This implies that you’re “adding” two modules, two state variables, two Panda tasks, … You could avoid this with something like:

if __name__ == 'src.importee':
  from panda3d.core import *
  import direct.directbase.DirectStart

  # this is the variable that gets printed to the console, sys.path screws it up
  state = False

  def enable():
    global state
    state = True

  def disable():
    global state
    state = False

  def stateLog(task):
    global state
    print state
    return task.cont
  
  taskMgr.add(stateLog, 'stateLog')

  base.accept('a',    enable)
  base.accept('a-up', disable)

Anyway I don’t know if the previous code is the best solution, maybe you should analyze your importing strategy better, I just wrote it to highlight the problem.

I’m not really sure why Python doesn’t think that I’m importing the same module. I mean I’m doing

import something

in both cases. Why does it assume that just because once it really is in the same folder, then it’s different?

Anyway, if you think I could get away by redesigning my importing strategy, then please feel free to help here:
I need to import modules which are 3 folders up from the current module’s folder and one folder down from there (in another subdirectory). I thought I could just add that folder to the sys.path to make things cleaner, but to be honest I’m not sure how the “unclean” import syntax would be either. All I know is to import a module in an upper directory you can do

import .something

To be honest, I would avoid the modification to sys.path, and would use import src.importee. That src prefix is meaningful, since it denotes that your importee module is in the src package (it is not a “path” information, how you arrange your packages and modules is important from a design point of view). If you don’t want to repeat that entire string every time in your code, you could import that module with import src.importee as i.

It’s not that I don’t want to type “package.module.var” each time, like you said there are ways around that, I just didn’t think it’s nice to type that much in the import statement each time.

But anyway, you didn’t answer how to import modules which are 3 folders up from the current module’s folder and one folder down from there (in another subdirectory). If I’ll know how to do that, maybe I’ll dump the idea with sys.path.

You could use:

from .... import importee

More info here (§6.4.2, the anchor doesn’t seem to work in my browser).

That’s typically considered bad coding practice, whereas there’s nothing wrong with specifying the whole package path (relative to the root of the tree). Have you ever seen Java code? :slight_smile: It’s much worse there.