Will Posted January 27, 2014 Share Posted January 27, 2014 (edited) 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 January 27, 2014 by Will Quote Link to comment
Miguel Barrera Posted January 27, 2014 Share Posted January 27, 2014 The only thing I can suggest if you have not done so is to add the paths to all modules under "Script Options". As far as "Import vs" goes, VW should find this module automatically without any adjustments if python was installed correctly at the default path. Quote Link to comment
Will Posted January 27, 2014 Author Share Posted January 27, 2014 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.... Quote Link to comment
Will Posted January 28, 2014 Author Share Posted January 28, 2014 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. Quote Link to comment
JBenghiat Posted January 28, 2014 Share Posted January 28, 2014 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 Quote Link to comment
Will Posted January 28, 2014 Author Share Posted January 28, 2014 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? Quote Link to comment
Justin Smith Posted February 4, 2014 Share Posted February 4, 2014 (edited) 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 February 4, 2014 by Justin Smith Quote Link to comment
Javier Posted May 15, 2014 Share Posted May 15, 2014 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 Quote Link to comment
Will Posted November 21, 2014 Author Share Posted November 21, 2014 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. Quote Link to comment
Recommended Posts
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.