Script to list all library script functions

I just thought I'd share this script which lists all of the functions included within a script library. I'm sure it could be improved (open to enhancements!) like including the list of arguments, and not using a string for the ScriptManager type... But it works for my purpose.

from com.inductiveautomation.ignition.designer.gui.tools.jythonconsole import JythonConsole
SYSTEM_FUNC_TYPE_STR = "<type 'com.inductiveautomation.ignition.common.script.ScriptManager$ReflectedInstanceFunction'>"
FUNC_TYPE_STR = "<type 'function'>"

def pyDocumenter(library, parentLibStr=''):
	"""
	This function prints the paths to all functions included within a script library.
	
	Example call:
	pyDocumenter(system)
	"""
	if parentLibStr == '':
		parentLibStr = library.name + '.'
	
	for obj_str in dir(library):
		obj = getattr(library, obj_str)
		obj_t_str = str(type(obj))
		
		# loop through script packages
		if isinstance(obj, JythonConsole.ConsolePackage):
			pyDocumenter(obj, parentLibStr + obj_str + '.')
		
		# document user functions
		elif isinstance(obj, JythonConsole.ConsoleModule):
			for mod_obj_str in dir(obj):
				mod_obj = getattr(obj, mod_obj_str)
				mod_obj_t_str = str(type(mod_obj))
				if mod_obj_t_str in (FUNC_TYPE_STR, SYSTEM_FUNC_TYPE_STR):
					print '{}{}.{}(...)'.format(parentLibStr, obj_str, mod_obj_str)
		
		# document `system` functions
		elif obj_t_str == SYSTEM_FUNC_TYPE_STR:
			print '{}{}(...)'.format(parentLibStr, obj_str)

pyDocumenter(shared)

Output:

system.alarm.acknowledge(...)
system.alarm.cancel(...)
system.alarm.createRoster(...)
system.alarm.getRosters(...)
system.alarm.getShelvedPaths(...)
system.alarm.listPipelines(...)
system.alarm.processPathCondition(...)
system.alarm.processStringEnumCondition(...)
system.alarm.queryJournal(...)
system.alarm.queryStatus(...)
system.alarm.shelve(...)
system.alarm.unshelve(...)
...

*** EDITED ***

8 Likes

If you’re on a new enough version, you can take advantage of the autocompletion system:

from com.inductiveautomation.ignition.designer.gui.tools import ScriptIndexer

ScriptIndexer.INSTANCE.cache

Will only work in the designer, though.

Maybe build an AST from the script code, get def tokens, and from there you can get arguments easily.
The ast module and its NodeVisitor class makes this simpler than it sounds.

1 Like

Not sure what the use case for this is but definitely a big old +1 for sharing a utility script with the community.

1 Like

I moved a number of script functions around, reorganising, and wanted to have a lookup from old location to new, without having to manually document it. So I could then manually refactor their references with find replace

I'll have to look into it, cheers!

1 Like

A function object from a script module has substantial information attached. Try this (and variations) in your script console:

def myFunc(arg1, *args):
	pass

for x in dir(myFunc):
	a = getattr(myFunc, x)
	print x, type(a), repr(a)

Note the stuff at the end:

__call__ <type 'builtin_function_or_method'> <built-in method __call__ of function object at 0x2>
__class__ <type 'type'> <type 'function'>
__closure__ <type 'NoneType'> None
__code__ <type 'tablecode'> <code object myFunc at 0x3, file "<input>", line 1>
__defaults__ <type 'NoneType'> None
__delattr__ <type 'builtin_function_or_method'> <built-in method __delattr__ of function object at 0x2>
__dict__ <type 'dict'> {}
__doc__ <type 'NoneType'> None
__ensure_finalizer__ <type 'builtin_function_or_method'> <built-in method __ensure_finalizer__ of function object at 0x2>
__format__ <type 'builtin_function_or_method'> <built-in method __format__ of function object at 0x2>
__get__ <type 'builtin_function_or_method'> <built-in method __get__ of function object at 0x2>
__getattribute__ <type 'builtin_function_or_method'> <built-in method __getattribute__ of function object at 0x2>
__globals__ <type 'dict'> {'a': {...}, '__builtins__': <module '__builtin__' (built-in)>, 'objScrHelpers': <module objScrHelpers at 4>, '__package__': None, 'system': <app package system at 5>, '__name__': '__main__', 'fpmi': <app package system at 6>, 'inspect': <module inspect at 7>, 'x': '__globals__', '__doc__': None, 'myFunc': <function myFunc at 0x2>}
__hash__ <type 'builtin_function_or_method'> <built-in method __hash__ of function object at 0x2>
__init__ <type 'builtin_function_or_method'> <built-in method __init__ of function object at 0x2>
__module__ <type 'str'> '__main__'
__name__ <type 'str'> 'myFunc'
__new__ <type 'builtin_function_or_method'> <built-in method __new__ of type object at 0x8>
__reduce__ <type 'builtin_function_or_method'> <built-in method __reduce__ of function object at 0x2>
__reduce_ex__ <type 'builtin_function_or_method'> <built-in method __reduce_ex__ of function object at 0x2>
__repr__ <type 'builtin_function_or_method'> <built-in method __repr__ of function object at 0x2>
__setattr__ <type 'builtin_function_or_method'> <built-in method __setattr__ of function object at 0x2>
__str__ <type 'builtin_function_or_method'> <built-in method __str__ of function object at 0x2>
__subclasshook__ <type 'builtin_function_or_method'> <built-in method __subclasshook__ of type object at 0x8>
func_closure <type 'NoneType'> None
func_code <type 'tablecode'> <code object myFunc at 0x3, file "<input>", line 1>
func_defaults <type 'NoneType'> None
func_dict <type 'dict'> {}
func_doc <type 'NoneType'> None
func_globals <type 'dict'> {'a': {...}, '__builtins__': <module '__builtin__' (built-in)>, 'objScrHelpers': <module objScrHelpers at 4>, '__package__': None, 'system': <app package system at 5>, '__name__': '__main__', 'fpmi': <app package system at 6>, 'inspect': <module inspect at 7>, 'x': 'func_globals', '__doc__': None, 'myFunc': <function myFunc at 0x2>}
func_name <type 'str'> 'myFunc'

Hmm, I did this already but for script library functions they don’t have __code__ or func_code which apparently will give you the arg names. Inspect also doesn’t like them because they’re not python functions but JythonConsole objects :thinking:
Any way to convert them to py function objects?
I found it strange also that the standard system library functions are different objects to user script library functions.
I also found it odd you can get the entire function definition for user functions as a string with function.code. That might come in handy one day, but then again, that’s what source control on the files themselves is for, so maybe not

Hmmm. They do for me. That is, user supplied jython.

I would not expect it for anything module-supplied under system. Their implementations are java.

What’s the type of your script library functions? Are you getting them as JythonConsole@ConsoleModules?

https://files.inductiveautomation.com/sdk/javadoc/ignition81/8.1.19/com/inductiveautomation/ignition/designer/gui/tools/jythonconsole/JythonConsole.ConsoleModule.html

The javadoc doesn’t have either of those props listed in there either, so I’m a bit confused

Hmmm. I was testing on a v8.1.13 VM that I had open anyways. Looks like this:

print type(inspect)
print type(inspect.introspect)

==>

<type 'com.inductiveautomation.ignition.designer.gui.tools.jythonconsole.JythonConsole$ConsoleModule'>
<type 'function'>

Ok, I get the same thing, but it's when I use getattr(inspect, 'introspect') that it becomes a JythonConsole object which no longer has func_code :confused:
Should that not return the same type of object?

Wait...... I stuffed up... I didn't go deep enough - my script only reports script modules, not the functions themselves.

Ok I fixed the script in the OP

1 Like

Paul, does this just get me the library names so I can run them through my function, or does this do more than that? (this is still useful, so thanks!)

Each entry in cache is a CompletionDescriptor, which has a variety of meta information, including a type. That type value (if it's not null) is itself a rich type description, with various attributes; e.g. name, description, and also a set of methods, attributes, and items (as in dictionary keys).

Each of those members are themselves instances of CompletionDescriptor, so they also might have a type (if the parsing is smart enough).

E.G:

from com.inductiveautomation.ignition.designer.gui.tools import ScriptIndexer

descriptor = ScriptIndexer.INSTANCE.cache['abc']

print descriptor
print descriptor.type
for method in descriptor.type.methods:
	print method
	print method.description
	print method.type
	for parameter in method.parameters:
		print parameter
		print parameter.type
1 Like

Awesome! I'll definitely have a look at this, thanks for the extra info

I expect TypeDescriptor & CompletionDescriptor to be the backbone of how script hints are populated in 8.3, @pturmel :wink: Currently the designer has to do some fairly fragile parsing of the HTML output of the PropertiesFileDocProvider to turn it into something useful, which is obviously limited in what it can do. Having proper return types from system functions would be a big bonus.