Jump to content
Developer Wiki and Function Reference Links ×

python import problem...


Will

Recommended Posts

OK so I'm writing a pretty complicated script and I don't want it all in one file.

I have a whole bunch of functions which are very simple and just wrap the original vs function. I want to put these in a file called wrappers.py. Like this:

import vs
import vso.base
import vso

def angToVec(angle, distance):
   v = vs.Ang2Vec(angle, distance)
   return Pt( v[0], v[1] )
   pass

def penLoc():
   p = vs.PenLoc()
   return Pt( p[0], p[1] )
   pass

def moveTo(pt):
   vs.MoveTo(pt.xy())
   pass

def lineTo(pt):
   vs.LineTo(pt.xy())
   pass

The directory structure of my package looks like this:

vso/
   __init__py
   base.py
   wrappers.py

To test this I have a script in a script palette in vector works which contains simply

import vso

If the above functions are in __init__.py, they work. If I paste them into wrappers.py and import them into __init__.py like this

for wrappers import * 

I get the error

Traceback (most recent call last):
  File "", line 4, in 
  File "/Users/will/vso/__init__.py, line 75, in polar
        v = angToVec(ang, dist)
  File "/Users/will/vso/wrappers.py", line 5, in angToVec
NameError: global name vs is not defined.

Even though, as you can see from the contents of my wrappers.py file above, I am clearly importing the vs module.

Python definitely allows importing the same module into multiple files. Is there some limitation in the Vectorworks implementation where I can only use import vs once per script?

Any other clues about whats going on here? This (and python modules in general) is driving me slightly mad... I wish you had chosen Ruby, its much cuter... :.-(

Edited by Will
Link to comment

Yes, thats what I thought at first but it has no trouble finding the vs module when its imported from __init__.py so this can't be the problem. It might be some subtlety of how the python imports modules but I have done extensive googling and I can't find a problem which is the same as this, some very similar but not this exact problem with importing an external module twice....

Link to comment

I think I found a bug in the Vectorworks implementation of the python interpreter which prevents it loading packages correctly.

To reproduce:

You should have a directory structure that looks like this:

~/vspython/
                 pytest/
                           __init__.py
                           sub/
                                 __init__.py
                                 a.py
                                 b.py

The contents of file a.py should be:

import sys
import pytest.sub.b

def callFinB():
   return pytest.sub.b.syspath()

The contents of file b.py should be:

import sys

def syspath():
   return("Message from b")

In a blank file create a new script palette

Create a new script in the script palette and set the language to python.

Click the scripting options button and add the ~/vspython directory to the python path

Paste the following into the script:

from pytest.sub import b
from pytest.sub import a
a.callFinB()

Click OK to close the script window to save. Double click the script in the script palette to execute it. I get an error. However, in an interactive python3 session I get the expected results as you can see here:

Bonobo:CAD scripts will$ python3
Python 3.3.3 (v3.3.3:c3896275c0f6, Nov 16 2013, 23:39:35) 
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from pytest.sub import b
>>> from pytest.sub import a
>>> print(a.callFinB())
Message from b
>>> 

The only workaround I can find is to put the entire contents of the script in one file and only import this.

Link to comment

I haven't checked your specific example, but using modules works for me.

Note that you will get an error with print. Because VW doesn't have a console, it used the error output dialog as stdout. Thus the script:

print("Hello world!")

will produce an error message containing:

Hello world!

You can try vs.Message() or vs.AlrtDialog() for testing. Note each module will have to import vs, and if your are testing, you need to use imp.reload() for palette scripts.

-Josh

Link to comment

I don't call print() in the code executed through vectorworks, only in the interactive shell in my terminal which am a using to demonstrate that the python code is correct and has works as intended when called by the official python 3.3 interpreter. It wouldn't make any difference if was calling vs.Message() or print() from the script executed in vectorworks because it barfs on the import statement before it has had a chance to get that far.

I can import single files individually, but I want to nest one module inside another and this doesn't work. Have you tried importing a module which imports another module?

I'm on OS 10.9 Are you running on windows or a different OS X?

Link to comment

Definitely can report that I'm having the same problem with importing other files in python.

Anyone find any workaround besides including all code in one file?

Update:

One of the other posters has a workaround in another thread:

import vs
import vs, test
from imp import reload
reload (test)

This reloads the test module and seems to solve my issues. Appears like vector works doesn't reload modules automatically.

Edited by Justin Smith
Link to comment
  • 3 months later...

I had the same issue but what appears to be actually happening is:

While vectorworks is running it has an instance of the python interpreter running so when you say import modulename modulename gets put into sys.modules. If you then make changes to modulename the interpreter wont reload the file which is why it needs to be reloaded as suggested by Justin above. Note that the run scripts in debug mode check box in the session preferences will only reload the plugin file script. Not any modules you've defined outside of vectorworks which are referenced by that script.

The easy way around this is to restart vectorworks every time you make a change which is annoying. The second is to reload manually as suggested above.

Or if you structure your plugin as in the tutorials so the first script contains

import _main
_main.execute()

you can use a decorator on your execute function to unload any modules loaded during the execute() call when it's finished. An example of such a decorator:

# decorator that causes any newly imported modules (i.e imported in decorated function)
# to have their reference count decremented i.e. del called on the module. So that
# they will be reloaded on their next import
import sys
from functools import wraps
def VWModuleReloader(f):

   #modified original from pyUnit 
   #http://pyunit.sourceforge.net/notes/reloading.html
   class RollbackImporter:
       def __init__(self):
           "Creates an instance and installs as the global importer"
           self.previousModules = sys.modules.copy()
           self.realImport = __builtins__['__import__']
           __builtins__['__import__'] = self._import
           self.newModules = {}

       def _import(self, name, globals=None, locals=None, fromlist=[],  level=0):
           result = self.realImport(name, globals, locals, fromlist,  level)
           self.newModules[name] = 1
           return result

       def uninstall(self):
           for modname in self.newModules.keys():
               if modname not in self.previousModules:
                   # Force reload when modname next imported
                   del(sys.modules[modname])
           __builtins__['__import__'] = self.realImport

   @wraps(f)
   def reloader(*args, **kwargs):

       rollbackImporter = RollbackImporter()
       try:
           f(*args, **kwargs)
       except:
           raise
       finally:
           rollbackImporter.uninstall()

   return reloader

To use it decorate the execute function in your _main.py

#Anything imported outside of decorated function won't be reloaded
from vwModuleReloader import VWModuleReloader
.
.
.
@VWModuleReloader
def execute():
    #any changes to a or b will be reloaded next time execute is called
    import a
    from b import C

Hope this helps

Link to comment
  • 6 months later...

Just got around to trying this and it works. This answer helped a lot, thanks.

By the way for anyone else out there having problems, note that if you use this technique any mistakes you make in importing modules can show up as pretty obscure errors. I had a problem where I was missing an __init__.py and the error thrown was completely unrelated.

Link to comment

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...