Python script: NameError: name 'Script' is not defined

Yet still you decided to follow much worse alternatives, just because they were at hand :confused:

Because they are standard, well understood parts of Windows which everyone is guaranteed to have.

Even if the ActiveScripting versions of Phyton followed the standards set by the first party languages and worked perfectly, we'd still discourage using them because everyone who wanted to use a script written in another language would have to install extra stuff, worry about version conflicts, etc., which just isn't worth it for what is usually a small difference in syntax that shouldn't matter for the typical length and complexity of a script.

(Being able to call DLLs is a valid thing, of course, but very niche. If it came to it, we could provide that capability to JScript and VBScript via our own objects.)

I've tried the latest beta and got a different error

:Error at line 43, position 0
sV = Script.vars
^
Traceback (most recent call last):
File "<Script Block >", line 43, in OnInit
  sV = Script.vars
...\win32comext\axscript\client\pyscript.py", line 105, in __getattr__
    return getattr(self._scriptItem_.dispatchContainer,attr)
...\win32com\client\dynamic.py", line 527, in __getattr__
    raise AttributeError("%s.%s" % (self._username_, attr))
AttributeError: Script.vars
 (0x80020009)

Is there a way to fix it?

FYI extracts of the code from the files mentioned:
Extract from pyscript

    def __getattr__(self, attr):
        # If a known subitem, return it.
        try:
            return self._scriptItem_.subItems[attr.lower()].attributeObject
        except KeyError:
            # Otherwise see if the dispatch can give it to us
            if self._scriptItem_.dispatchContainer:
#Next Line is #105
                return getattr(self._scriptItem_.dispatchContainer,attr) 
        raise AttributeError(attr)

Extract from dynamic.py

# Delegate to property maps/cached items
retEntry = None
if self._olerepr_ and self._oleobj_:
    # first check general property map, then specific "put" map.
    retEntry = self._olerepr_.propMap.get(attr)
    if retEntry is None:
        retEntry = self._olerepr_.propMapGet.get(attr)
    # Not found so far - See what COM says.
    if retEntry is None:
        try:
            if self.__LazyMap__(attr):
                if attr in self._olerepr_.mapFuncs: return self._make_method_(attr)
                retEntry = self._olerepr_.propMap.get(attr)
                if retEntry is None:
                    retEntry = self._olerepr_.propMapGet.get(attr)
            if retEntry is None:
                retEntry = build.MapEntry(self.__AttrToID__(attr), (attr,))
        except pythoncom.ole_error:
            pass # No prop by that name - retEntry remains None.

if not retEntry is None: # see if in my cache
    try:
        ret = self._mapCachedItems_[retEntry.dispid]
        debug_attr_print ("Cached items has attribute!", ret)
        return ret
    except (KeyError, AttributeError):
        debug_attr_print("Attribute %s not in cache" % attr)

# If we are still here, and have a retEntry, get the OLE item
if not retEntry is None:
    invoke_type = _GetDescInvokeType(retEntry, pythoncom.INVOKE_PROPERTYGET)
    debug_attr_print("Getting property Id 0x%x from OLE object" % retEntry.dispid)
    try:
        ret = self._oleobj_.Invoke(retEntry.dispid,0,invoke_type,1)
    except pythoncom.com_error as details:
        if details.hresult in ERRORS_BAD_CONTEXT:
            # May be a method.
            self._olerepr_.mapFuncs[attr] = retEntry
            return self._make_method_(attr)
        raise
    debug_attr_print("OLE returned ", ret)
    return self._get_good_object_(ret)

# no where else to look.
# Next line is 527
    raise AttributeError("%s.%s" % (self._username_, attr))

I have no idea what it even means :slight_smile:

Script object does not have vars attribute.Why do you think it should? What are you actually doing and how?

It's documented as having one here, at least:

https://www.gpsoft.com.au/help/opus12/index.html#!Documents/Scripting/Script.htm

Join the club :wink:

They do, and Script.vars.get("ConfigSettingName") allows me to use user config values (with this helper script), which is what I'm trying to do by pasting some of the plumbing from my JS scripts

SOA#1 (Polish for Standard Administrator's Answer #1 :slight_smile: ) - WOMPC

"Script object does not have vars attribute" -> I meant that this is what the error means.

Hm, I think I haven't made any typos, but this does nothing on the latest beta (12.20.8, but also tried with the pre-script beta 12.20.5), so Doesn'tWOMPC :slight_smile:.
I also don't really understand why it would do anything since in the column scripts the initData._FlagAsMethod('AddColumn') works, i.e. the col.method is invoked, only when columns are refershed
Is the initData._FlagAsMethod('AddCommand') supposed to be called on every script refresh automatically (or is there some other event there that triggers it)? And if I add a direct call in the OnInit function OnTestCmd(""), I get the same attribute error mentioned above
However, when I change Script.vars to DOpus.vars (but still I need to add an OnTestCmd("") in the OnInit function, I get this log

...TestPy.pys:  script_cmd_data
...TestPy.pys:  test

Test script I'm using

def OnInit(initData): #Init script
  initData.name          	= "test";
  initData.version       	= "1.0";
  initData.copyright     	= "20";
  # initData.url         	= "";
  initData.desc          	= "test";
  initData.default_enable	= True;
  initData.min_version   	= "12.5";

  initData._FlagAsMethod('AddCommand')
  cmd       	= initData.AddCommand()
  cmd.name  	= 'TestCmd'
  cmd.method	= 'OnTestCmd'
  cmd.desc  	= initData.desc
  cmd.label 	= 'TestCmd'

def OnTestCmd(script_cmd_data):
  test = Script.vars
  for v in vars():
    DOpus.Output(v)

Sorry, didn't understand it

OK, first, there is stupid mistake in my sample, it should be for "v in test". It does not print out anything then, but I guess this is expected (or not?) - there is no error though.
Second, OnInit is called automatically by DOpus on script refresh (that is when loading DOpus or when script file is saved - maybe there are some other occasions as well). You don't call it manually.
Third, the sample below works without errors, it does not print anything either from Script.vars or DOpus.vars, but I think this is expected. It does not fail silently either. DO version: Directory Opus Pro 12.20 Build 7394 x64

Note win32traceutil import. It allows your to print() in order to debug - you can see output in PythonWin> Tools> Trace Collector Debugging Tool

import win32traceutil

# Called by Directory Opus to initialize the script
def OnInit(initData):
	initData.name = "test";
	initData.version = "1.0";
	initData.copyright = "(c) 2020 Łukasz";
#	initData.url = "https://resource.dopus.com/viewforum.php?f=35";
	initData.desc = "test";
	initData.default_enable = True;
	initData.min_version = "12.0";

	initData._FlagAsMethod('AddCommand')
	cmd = initData.AddCommand()
	cmd.name = 'TestCmd'
	cmd.method = 'OnTestCmd'
	cmd.desc = initData.desc
	cmd.label = 'TestCmd'


def OnTestCmd(script_cmd_data):
	DOpus.Output(1)
	test = Script.vars
	DOpus.Output(2)
	for v in test:
		DOpus.Output(v)
	DOpus.Output(3)
	test = DOpus.vars
	DOpus.Output(4)
	for v in test:
		DOpus.Output(v)
	DOpus.Output(5)

I call it from this button:

<?xml version="1.0"?>
<button backcol="none" display="both" textcol="none">
	<label>Nowy przycisk</label>
	<icon1>#newcommand</icon1>
	<function type="normal">
		<instruction>TestCmd</instruction>
	</function>
</button>
1 Like

there is no error because the script simply doesn't execute that part. Once I add a call to that function OnTestCmd(""), then it shows an error

Yeah, that part I know, I just had to connection to how that is relevant to another, not OnInit, function (now that you mention a button, it's clear — the button calls on this part)

Let me check your script later and see if it works...

Yeah, I always include it, that's where I see the vars created and such that I mentioned earlier, but didn't know about the fact taht print() outputs there, thanks, that's very helpful in trying to understand how things work!!!

The script executes to the end, as you can see via numbers 1-5 printed to DOpus log panel.
How do you call OnTestCmd? It is a scripted DOpus command, so it should be called like a built-in command.
What is the actual code you run and how do you run it?

It does work now, thanks! I was just saying that previously I simply forgot I needed a button to run your script example

I've also already been able to do some successful testing:

  • setting/getting script.vars
  • writing to Script.config.Name1 and then reading from it
  • creating a Map to store config description, although had to confusingly use different types of parentheses (assign myMap["MapKey"] vs read myMap("MapKey"))
DOpus._FlagAsMethod('Create')
factory = DOpus.Create()
factory._FlagAsMethod('Map')
myMap = factory.Map()
myMap("MapKey") = "MapValue" # cannot assign to function call (0x80020009)
myMap["MapKey"] = "MapValue" # Works
DOpus.Output(myMap["MapKey"]) # in32com\client\dynamic.py", line 257, in __getitem__: raise TypeError("This object does not support enumeration")
DOpus.Output(myMap("MapKey")) # Works

So it looks very promising :slight_smile:

Here is a post where some scripting surprises are explained (like using () for collections) Scripts (Python): Getting up to speed with Python + sample script
You may want to avoid DOpus pseudo-types (like Map), however I am not sure whether dict or list would be accepted by DOpus in place of them (you would need to do a conversion anyway).

Thanks, I actually have this post copied to my local guide for reference (I mean, that's where I got those _FlagAsMethods from), just haven't read it completely and thoroughly enough and didn't know the answer was already there :slight_smile:

doesn't seem like it, I get objects of type 'dict' can not be converted to a COM VARIANT

You mean something like this?

dict = {'DEBUG': 'DbgDescription1', 'DEBUG2': 'DbgDescription2'}
myMap = factory.Map()
for i, k in enumerate(dict):
  myMap[k] = dict[k]
inputData.config_desc = myMap

Is this still preferable over using the Map directly (also curious to know why)?

Regarding using python types vs DOpus, it's just a matter of convenience in fact, but it's easy to fall into DOpus types hole when you forget that they really are just necessary COM-related workarounds.
I will try to add some smart handling of DOpusFactory stuff in dopus_proxy.

By the way, even when I define some config initData.config.CfgName1 that are supposed to be also attached to the Script.config, this fails:

for v in Script.config:
  DOpus.Output(v)

...so I can't enumerate all my config Names and also can't get them like Script.config("CfgName1")(I get COM Error: Invalid index) instead of Script.config.CfgName1 (I thought I could store config values in one loop instead of having to manually address each)

This is expected, right, due to these COM workarounds and such?

Can you paste the whole block of code? Hard to follow what you have on mind.

See comment #1, #2, #3 in OnTestCmd1 (this is simplified, I use Sv.set("hideAttr", Sc.hideAttr) type of commands in a separate function that can be called in def OnScriptConfigChange(configChangeData): to update script vars)

import win32traceutil
def OnInit(iData): #Init script
  iData.name          	= "test";
  iData.version       	= "1.0";
  iData.copyright     	= "20";
  # iData.url         	= "";
  iData.desc          	= "test";
  iData.default_enable	= True;
  iData.min_version   	= "12.5";

  iData.config.DebugOutput = True;
  iData.config.DEBUG = "dbgF";
  iData.config.hideAttr = "a"
  DOpus._FlagAsMethod('Create')
  factory = DOpus.Create()
  factory._FlagAsMethod('Map') # Map["Set"]= "" # Map("Get")
  dict = {'DEBUG': 'DbgDescription', 'hideAttr': 'hideAttrDescription'}
  myMap = factory.Map()
  for i, k in enumerate(dict):
    myMap[k] = dict[k]
  iData.config_desc = myMap

  iData._FlagAsMethod('AddCommand')
  cmd         = iData.AddCommand()
  cmd.name    = 'TestCmd1'
  cmd.method  = 'OnTestCmd1'
  cmd.desc    = iData.desc
  cmd.label   = 'TestCmd1'

def OnTestCmd1(scriptCmdData):
  Sv, Sc = Script.vars, Script.config

  #1# I can manually set script variables rom script configs
  Sv.set("hideAttr", Sc.hideAttr)
  # Sv.set("DEBUG", Sc.DEBUG)
  # #... and later retrieve them
  dbg(Sv.get("hideAttr"))

  #2# But I can't enumerate Script.config
  for cfg in Sc:
    dbg(cfg)
  # And I can't set in a way that would accept strings
  Sv.set("hideAttr", Sc["hideAttr"]) #TypeError: This object does not support enumeration
  Sv.set("hideAttr", Sc("hideAttr")) # COM Error: Invalid index.

  #3# So I can't get iterate config properties to save them like so
  for cfg in Sc:
    Sv.set(cfg, Sc(cfg))
  dbg(Sv.get("DEBUG")) # Fails

def dbg(text):
  try:
    if (Script):
      pass
    if (0 or Script.config.DebugOutput):
      DOpus.Output(str(text))
  except Exception as e:
    DOpus.Output(str(type(e)))
    print(e) # print(e.hresult, e.strerror, e.excepinfo, e.argerror)
    DOpus.Output(str(e))

I don't have the beta DO, so I cannot play with Script, but some remarks:

  • Script.config looks like a namespace, not collection, so no surprise it's not iterable. No surprise that you cannot access it with setitem/call notation either.
  • To avoid all this conundrums, I would simply pickled/shelved my stuff into known location. As all this would probably remain in disk cache throughout all script execution, filesystem overhead of accessing data would be totally negligible.
  • If you insist on Script API, put there a known item with index of all other stuff you attach to it.