tag:infinitekind.tenderapp.com,2009-01-14:/discussions/moneydance-development/6482-importing-a-python-file-into-anotherInfinite Kind: Discussion 2021-08-25T00:31:04Ztag:infinitekind.tenderapp.com,2009-01-14:Comment/493415832021-08-16T08:27:31Z2021-08-17T02:28:16ZImporting a python file into another<div><p>Take a look at: 'print globals()'</p>
<p>This code worked:</p>
<pre>
<code>import importlib, os, sys
pyfilepath = "/Users/stu/Documents/Moneydance/My Python Scripts/test_var.py"
if not os.path.exists(pyfilepath): raise Exception("ERROR")
dirname, basename = os.path.split(pyfilepath) # pyfilepath: '/my/path/mymodule.py'
sys.path.append(dirname) # only directories should be added to PYTHONPATH
module_name = os.path.splitext(basename)[0] # '/my/path/mymodule.py' --> 'mymodule'
module = importlib.import_module(module_name) # name space of defined module (otherwise we would literally look for "module_name")
for var in globals(): print var
print ""
print module.greet1("Stuart")</code>
</pre></div>Stuart Beesley (Mr Toolbox)tag:infinitekind.tenderapp.com,2009-01-14:Comment/493415832021-08-17T02:25:42Z2021-08-17T02:25:42ZImporting a python file into another<div><p>Thanks, Stuart (as always).<br>
I thought it might have something to do with the path, but didn’t know about import_module.<br>
I’ll explore things four then when I get home later today.<br>
Thanks again…..</p></div>davidcullentag:infinitekind.tenderapp.com,2009-01-14:Comment/493415832021-08-17T02:32:46Z2021-08-17T02:32:46ZImporting a python file into another<div><p>I didn’t play with the std ‘import’ as you probably need to specify a file path and for this you need to call importlib (which ‘import’ actually calls). I have read something else where you can import a path using ‘.’s instead of ‘/‘s to separate the path folders, but haven’t tried. Good luck.</p></div>Stuart Beesley (Mr Toolbox)tag:infinitekind.tenderapp.com,2009-01-14:Comment/493415832021-08-17T07:55:48Z2021-08-17T07:55:48ZImporting a python file into another<div><p>Stuart,<br>
Thanks for your help. Things are working now, but not quite.<br>
When I import the module as you suggest, with<br></p>
<pre>
<code>module = importlib.import_module(module_name)</code>
</pre>
it seems to work, and:<br>
<pre>
<code>print dir(module)</code>
</pre>
shows the module function, which I have renamed as <strong>greet(who)</strong><br>
This then works fine when calling:<br>
<pre>
<code>module.greet(“Stuart")</code>
</pre>
My problem is that if I then rename the module function to, say, <strong>greet2()</strong>, the ** print dir(module)** command continue to show the (now renamed) <strong>greet()</strong> and not the new <strong>greet2()</strong>.<br>
Calling <strong>module.greet(“Stuart")</strong> continues to work (even though the function has been renamed) but calling <strong>module.greet2(“Stuart”</strong> fails with:<br>
<pre>
<code>AttributeError: 'module' object has no attribute ‘greet2'</code>
</pre>
It would seem that the old module has been cached? but I don’t know how to force it to refresh to reflect the new function edit?</div>davidcullentag:infinitekind.tenderapp.com,2009-01-14:Comment/493415832021-08-17T08:04:25Z2021-08-17T08:04:25ZImporting a python file into another<div><p>I can't help without seeing your actual code. I suspect you are trying to import the same module twice, which won't work....</p></div>Stuart Beesley (Mr Toolbox)tag:infinitekind.tenderapp.com,2009-01-14:Comment/493415832021-08-18T02:40:57Z2021-08-18T02:40:57ZImporting a python file into another<div><p>Stuart,<br>
Thanks for the hint. After googling, I was able to solve things with:<br></p>
<pre>
<code>module = imp.reload(module)</code>
</pre>
Not sure if there is a better (more correct) way.</div>davidcullentag:infinitekind.tenderapp.com,2009-01-14:Comment/493415832021-08-18T04:42:33Z2021-08-18T04:42:33ZImporting a python file into another<div><p>I’m guessing that you would not really rename a module during the life of a runtime script? Without knowing what you are trying to do or why it’s difficult to comment further. As you say, reload, reruns the import file if that’s what you want to do.</p></div>Stuart Beesley (Mr Toolbox)tag:infinitekind.tenderapp.com,2009-01-14:Comment/493415832021-08-18T09:36:33Z2021-08-18T09:36:33ZImporting a python file into another<div><p>You are correct that I do not really want to rename a module during the life of a runtime script (once the module has been written).<br>
I am currently writing the module and testing it on the fly in Moneydance MoneyBot console and as I go I make editing changes to the module which I then test to see that they are working.<br>
Your earlier code has helped me with the initial loading of my module, but I need to call <strong>imp.reload(module)</strong> to test that my edit changes correctly flow though.<br>
Once things are finalised, I no longer need to <strong>reload</strong> things and your code then works fine.<br>
I hope this makes things clearer, Thanks for your guidance.<br>
PS: one final thing: my module depends on the <strong>moneydance</strong> variable (moneydance.getCurrentAccount().getAccountByName('name')) but I am getting an error message that global <strong>moneydance</strong> variable is not defined (even though (moneydance.getCurrentAccount().getAccountByName('name') works if on the main script (it only fails on my module). I worked around tis by passing <strong>moneydance</strong> to the module function (in addition to the account name).</p></div>davidcullentag:infinitekind.tenderapp.com,2009-01-14:Comment/493415832021-08-18T10:06:51Z2021-08-18T10:06:51ZImporting a python file into another<div><p>That'll be a scope 'issue' of some sort.</p>
<p>You could try doing this at the start of both your script, and also your module (outside any def statements).</p>
<pre>
<code>global moneydance</code>
</pre>
<p>and then see if your module can see it. Or you might have to try main.moneydance. Within your module do <code>print globals()</code> and see if it's there somewhere..</p></div>Stuart Beesley (Mr Toolbox)tag:infinitekind.tenderapp.com,2009-01-14:Comment/493415832021-08-19T05:47:23Z2021-08-19T05:47:23ZImporting a python file into another<div><p>Stuart,<br>
Thanks for your persistence. Here is my full code.<br>
The main file:<br></p>
<pre>
<code>#main.py
#!/usr/bin/python
#-<em>- coding: UTF-8 -</em>-
global moneydance
import imp, importlib, os, sys
script = "/Users/david/Documents/md/myModule.py"
dirname, basename = os.path.split(script)
sys.path.append(dirname)
module_name = os.path.splitext(basename)[0]
module = importlib.import_module(module_name)
module = imp.reload(module) # reload to reflect module edits
print module.getAcctForName('EM1')</code>
</pre>
and the module:<br>
<pre>
<code>#myModule.py
#!/usr/bin/python
#-<em>- coding: UTF-8 -</em>-
global moneydance
''' Gets account from name; checks that it is valid. '''
account = moneydance.getCurrentAccount().getAccountByName(name)
assert type(account).<strong>name</strong> == 'Account', \
"{} is not a valid Account - check spelling of FullAccountName".format(name)
return account</code>
</pre>
Adding <strong><em>global moneydance</em></strong> to either/both files does not seem to help. I still get an error: "NameError: global name 'moneydance' is not defined.
<p>If Ipass moneydance as a parameter when I call the module function, (ie changing things to:<br></p>
<pre>
<code>def getAcctForName(name, moneydance):</code>
</pre>
everything works.<br>
I do not quite understand what you mean by <strong><em>try main.moneydance</em></strong>. When I try:<br>
<pre>
<code>account = moneydance.getCurrentAccount().getAccountByName(name)</code>
</pre>
I get <strong><em>NameError: global name 'main' is not defined</em></strong><br>
Finally, when I do <strong><em>print list(globals())</em></strong> in myModule.py, I get:<br>
<code>__file__ __name__ getAcctForName __builtins__ x __doc__ __package__</code><br>
but I do not see any reference to <strong><em>moneydance</em></strong>.<br>
I’d like to resolve things completely (so I understand more about Python), but if I cannot, then passing <strong><em>moneydance</em></strong> as a parameter (which works) is acceptable.</div>davidcullentag:infinitekind.tenderapp.com,2009-01-14:Comment/493415832021-08-19T06:18:57Z2021-08-19T09:19:23ZImporting a python file into another<div><p>I do like the early morning tests...!</p>
<p>It is a scope issue. Globals are global only to the module they were defined within. In Python 3 you can add the globals={} parameter to the importlib.import_module() call, but not in 2.7.</p>
<p>You can cheat however, and given this is preset by MD anyway.... Stick this line in after your import statement, and before you call the module/function:<br></p>
<pre>
<code>module.moneydance = moneydance</code>
</pre>
This will set the variable within your module.
<p>NOTE: I don't like this line:<br></p>
<pre>
<code>assert type(account).name == 'Account', "{} is not a valid Account - check spelling of FullAccountName".format(name)</code>
</pre>
What are you trying to do? To check you have an account object do this:<br>
<pre>
<code>isinstance(account, Account)</code>
</pre>
to make this work, you will need to do this at the beginning of your script:<br>
<pre>
<code>from com.infinitekind.moneydance.model import Account</code>
</pre>
<p>... or</p>
<pre>
<code>account.getAccountName()</code>
</pre>
for the name
<p>😉</p></div>Stuart Beesley (Mr Toolbox)tag:infinitekind.tenderapp.com,2009-01-14:Comment/493415832021-08-19T09:17:08Z2021-08-19T09:17:08ZImporting a python file into another<div><p>Stuart,<br>
This is really very helpful (and instructive). I have learned a lot from your guidance. I’ll play with things more tomorrow, and will change over to <strong><em>is instance(account, Account)</em></strong>.</p></div>davidcullentag:infinitekind.tenderapp.com,2009-01-14:Comment/493415832021-08-19T09:20:32Z2021-08-19T09:20:32ZImporting a python file into another<div><p>to clarify, it's <code>if isinstance():</code> - the damn spell checker inserted a space..... 😊</p></div>Stuart Beesley (Mr Toolbox)tag:infinitekind.tenderapp.com,2009-01-14:Comment/493415832021-08-20T05:14:52Z2021-08-20T05:14:52ZImporting a python file into another<div><p>Changes made. All now working fine. Reload now with <strong>implib</strong>:<br></p>
<pre>
<code>reload(sys.modules[moduleName]) # reload to reflect any edits</code>
</pre>
Thanks again......</div>davidcullentag:infinitekind.tenderapp.com,2009-01-14:Comment/493415832021-08-23T05:33:18Z2021-08-23T05:33:18ZImporting a python file into another<div><p>Stuart,<br>
A final note.<br>
Your earlier guidance has been really useful to me in understanding about <strong>sys.path. importlib</strong> (and <strong>imp</strong> ) as well as <strong>globals()</strong>.<br>
Your code is very instructive/useful for importing a module (file) from a directory which is <strong>not</strong> already in <strong>PYTHONPATH</strong>.<br>
However, for importing a module in the same directory (already in <strong>PYTHONPATH</strong> I have solved things (probably obvious to a more experienced Python user then me), as follows:<br></p>
<pre>
<code>import sys, myModule
reload(sys.modules[myModule.<strong>name</strong>])</code>
</pre>
My main “problem” (now solved by your insight) was not realising that my module needs to be reloaded each time to reflect editing changes I am making “on the fly”.</div>davidcullentag:infinitekind.tenderapp.com,2009-01-14:Comment/493415832021-08-23T05:49:40Z2021-08-23T05:49:40ZImporting a python file into another<div><p>Yes, it's not obvious that 'import' means run the file, and that if you call import again, Python remembers you've done this before and won't re-run the file....</p>
<p>Also, note. Your imported files should always have this 'guard' in place wrapping any code that gets executed:</p>
<pre>
<code>if __name__ == "__main__":
<execute some code></code>
</pre>
<p>You don't need this if you are just defining things like variables, methods or classes. But you should if you have code that will/could execute..</p>
<p>Google 'if <strong>name</strong> == <a href="'"><strong>main</strong></a>; for more details..... 😉</p></div>Stuart Beesley (Mr Toolbox)