views:

185

answers:

4

I'm trying to import a module, while passing some global variables, but it does not seem to work:

File test_1:

test_2 = __import__("test_2", {"testvar": 1})

File test_2:

print testvar

This seems like it should work, and print a 1, but I get the following error when I run test_1:

Traceback (most recent call last):
  File ".../test_1.py", line 1, in <module>
    print testvar
NameError: name 'testvar' is not defined

What am I doing wrong?

EDIT:

As I commented later on, this is an attempt to replace the functions within a graphics library. Here is a sample program (that my teacher wrote) using that library:

from graphics import *
makeGraphicsWindow(800, 600)

############################################################
# this function is called once to initialize your new world

def startWorld(world):
    world.ballX = 50
    world.ballY = 300
    return world

############################################################
# this function is called every frame to update your world

def updateWorld(world):
     world.ballX = world.ballX + 3
     return world

############################################################
# this function is called every frame to draw your world

def drawWorld(world):
    fillCircle(world.ballX, world.ballY, 50, "red")

############################################################

runGraphics(startWorld, updateWorld, drawWorld)

Note that this code is designed such that people who have never (or almost never) seen ANY code before (not just python) would be able to understand with little effort.

Example for rewriting the function:

Original code:

def drawPoint(x, y, color=GLI.foreground):
    GLI.screen.set_at((int(x),int(y)), lookupColor(color))

Injected code:

# Where self is a window (class I created) instance.
def drawPoint(x, y, color = self.foreground):
  self.surface.set_at((int(x), int(y)), lookupColor(color))

I guess my real question is: how would I inject global functions/variables into an imported module before the module runs...?

A: 

You are trying too hard you just need simple imports Define the variable in one module , say test1

testvar = 1

in test2 put

import test1
print test1.testvar
Mark
One problem: test2 must remain as-is; I have no control over it and I can't have an additional line of code in it. Also, I need to import it from test1.
CMC
+3  A: 

As the docs explain, the globals parameter to __import__ does not "inject" extra globals into the imported module, as you appear to believe -- indeed, what's normally passed there is the globals() of the importing module, so such injection would be highly problematic if it happened. Indeed, __import__'s docs specifically say:

The standard implementation does not use its locals argument at all, and uses its globals only to determine the package context of the import statement.

Edit: if you do need injection, e.g. into an empty imported module, as the OP suggested in a comment, that's easy as long as you're OK with doing it right after the import:

themod = __import__(themodname)
themod.__dict__.update(thedict)

The imported module's body will not be aware of still-to-happen injections, but that's clearly no matter if said body is empty anyway;-). Right after the import, you can inject to your heart's contents, and all following uses of that module will see your injections as if they were bona fide module-level bound names (because they are;-).

You could even, if you wished, save the need for an empty .py module to start with...:

import new, sys
themod = new.module(themodname)
sys.modules[themodname] = themod
themod.__dict__.update(thedict)

Edit: the OP tries to clarify in an edit of the Q...:

I guess my real question is: how would I inject global functions/variables into an imported module before the module runs...?

"The module runs" doesn't make much sense -- modules are loaded (which executes their body), just once (unless re-loaded explicitly later)... they never "run" per se. Given an empty module, you first import it (which executes nothing since the module's empty), then you can if you wish use a mix of themodule.__dict__.update and attribute assignment to populate the module -- functions are not automatically called when just mentioned (as the OP expresses fears they will be in a comment), so they can be treated just like any other variable in this respect (and most others, which is why Python's said to have first class functions).

Alex Martelli
That helps, but how do I accomplish that?
CMC
I suspect it might help to explain in higher level terms what you are trying to do.
Mark
Okay. I am trying to replace a module written by my teacher, so that the file that would normally be imported is empty, and I give functions specific to the environment to replace these, while still allowing the program to run.More specifically, he wrote a graphics library (dubbed graphics.py) and I am trying to create an artificial windows system that has the exact same function calls as his library.EDIT: this library "simplifies" PyGame.
CMC
If you're OK with performing the injection _after_ the import, that's easy -- let me edit the answer to show that.
Alex Martelli
I would be okay, however, there is a function: runGraphics that is called as soon as the module is imported, and is the main contender for priority of re-implementation.
CMC
@CMC, you can inject functions just as well as any other object, of course -- just have `{'runGraphics': lambda: None}` (or whatever... probably with a real function in lieu of the `lambda`, I guess;-) as part of all of the dictionary `thedict` you're injecting from.
Alex Martelli
@CMC, code is (by design) unreadable in comments -- edit your original question to add well-formatted and therefore readable code.
Alex Martelli
Sorry about that.@Alex, wouldn't runGraphics be called before I could inject the code?
CMC
I'm not sure I understand your last code snippet...it appears to be doing some kind of extended import...
CMC
@CMC, no, a function is called only when you apply the call operator to it (i.e. place parentheses after it, with or without args in between). And, there's nothing "extended" about the import in my latest snippet: indeed in the latest snippet there's **no** import whatsoever -- it makes a new module object, sticks it in sys.modules so that future imports will find it, and populates it.
Alex Martelli
just a note: runGraphics IS called in the module, as demonstrated in my edited first post.
CMC
@CMC, you said in your first comment in this thread "the file that would normally be imported is empty" -- if so then it can't contain any call either. Your edited **question** (assuming that's what you mean by "first post"?!) doesn't show anything about the contents of `graphics.py`, the module being imported, and in particular does not show it containing any calls to `runGraphics` -- I see the call only in an **importing** module, so your continuing objections are, simply, impossible to understand (and the solutions I've proposed would work -- unless you're hiding yet more from us;-).
Alex Martelli
Sorry, I'm not that good at explaining in writing. In my edit for the question, I posted some code, and the last line is a call to runGraphics. Graphics.py contains a bunch of functions I'm trying to rewrite as Localized to a window I have created (a window within a window, so to speak), and I have it so that graphics.py is an empty file so that the line, "from graphics import *" does nothing. I'm trying to inject replacements for what would come from graphics.py normally, so that I don't need to change anything in the file itself and it can run as-is.
CMC
Documentation for the module is here: http://inside.catlin.edu/site/compsci/ics/python/PythonGraphics.html
CMC
In your edit, you comment on my "the module runs" by that I mean: "the module executes" because the module I'm talking about is the same ball program, not the graphics.py file.
CMC
@CMC, I repeat: if `'graphics.py` is empty, and _anywhere_ in your code before the `from graphics import *`, you perform the injection into the `graphics` module of all needed functions such as `runGraphics`, the code you show **will** "run as-is" (despite the horrible, **horrible** `import *`, which makes every Python lover cringe with disgust and horror -- as must have been explained at least 100 times by now in both Stack Overflow and elsewhere -- I've already **repeatedly** given you powerful techniques to achieve all that, and am sick and tired of having to repeat myself).
Alex Martelli
+1  A: 

As said, the __import__ function doesn't add globals to a new module. If you want to do that, I'd suggest adding some kind of initialize() function that assigns a value to a pre-declared global table, like this:

gTable = {}
gInited = False

def initialize(table):
    if(not gInited):
        gTable = table
        gInited = True

def getGlobal(name):
    if(name in gTable):
        return gTable[name]
    else:
        throw NameError("name '"+name+"' is not defined")

def setGlobal(name, value):
    gTable[name] = value

def mod_main():
    print getGlobal("testvar")

and import it like this:

import modGlobalsTest
modGlobalsTest.initialize({"testvar": 1})
modGlobalsTest.mod_main()

If you want to change the functions in the imported library, you can do something like this:

import foobar
def bar(x):
    return x+1
foobar.foo = bar

to replace foobar.foo with the custom function bar.

computergeek6
That wouldn't work: as I already commented on a different post, I cannot change the code in the module I am importing.
CMC
Involving EDIT: That would work, except that by that point, I would already have reached a NameError with runGraphics.
CMC
A: 

It looks like you want to inject code into the graphics library and run the example programs using that. If it is so, you can do it like this:

import graphics
graphics.drawPoint = myDrawPoint

You can also write a program that takes the filename of the example as argument and executes that:

import sys
import graphics

def main():
    graphics.drawPoint = myDrawPoint
    execfile(sys.argv[1])
Anand Chitipothu