Hi there,
I've run into a super strange error with my Python SDK tool: I get errors and strange behaviors when the tool is present multiple times in the workflow.
My tool has a GUI option to select different modes: List Files or Download Files. This is stored as a DataItem named ToolMode and read in the AyxPlugin.pi_init() method:
class ToolMode(Enum):
NONE_MODE = 0
LIST_FILES = 1
DOWNLOAD_TO_PATH = 2
DOWNLOAD_TO_BLOB = 3
class AyxPlugin:
def __init__(self, n_tool_id: int, alteryx_engine: object, output_anchor_mgr: object):
# Default properties
self.n_tool_id = n_tool_id
self.alteryx_engine = alteryx_engine
self.output_anchor_mgr = output_anchor_mgr
# Tool mode: What should we do?
self.tool_mode = ToolMode.NONE_MODE
def pi_init(self, str_xml: str):
setting_tree = Et.fromstring(str_xml)
self.tool_mode = {
'list': ToolMode.LIST_FILES,
'download_file': ToolMode.DOWNLOAD_TO_PATH,
'download_blob': ToolMode.DOWNLOAD_TO_BLOB
}.get(setting_tree.find('ToolMode').text, ToolMode.NONE_MODE)
At a different method I check which self.tool_mode I have and decide what to do:
def pi_push_all_records(self, n_record_limit: int) -> bool:
self.output_message("Debug: {}".format(self.tool_mode))
if self.tool_mode == ToolMode.LIST_FILES:
# Fields only present for LIST_FILES mode
self.output_recordinfo[field_dict['UID']].set_from_string(record_creator, f['uid'])
else:
# Download files if not in LIST_FILES mode
if self.tool_mode == ToolMode.DOWNLOAD_TO_BLOB:
# Generate temporary filename
out_fname = self.alteryx_engine.create_temp_file_name('tmp')
# Download file
try:
# Download file to temporary folder
sftp_conn.get(f['filename'], localpath=out_fname)
except IOError as e:
self.output_message('Error transferring file "{}": {}'.format(f['filename'], e))
Everything works as expected as long as I have the tool only once on the canvas. As soon as I have two or more instances of the tool, strange behavior starts. In particular, in pi_push_all_records() I get an error that out_fname is not defined -- despite the fact that tool should not even run this chunk of code. The output correctly shows "Debug: ToolMode.LST_FILES", but the subsequent "if" does not catch this, but runs into the "else" branch.
This behavior does not make any sense. It seems like Alteryx is messing up the objects and properties are shared between instances. But I have checked very carefully to use "self.tool_mode" everywhere. So, there should be absolutely no global objects present.
Did anyone ever epexierence something similar?
Best
Christopher
Solved! Go to Solution.
Problem solved...
In my post I claimed
But I have checked very carefully to use "self.tool_mode" everywhere. So, there should be absolutely no global objects present.
but this isn't actually true! ToolMode as a class is defined as a global object. As soon as a second instance of the tool is placed on the canvas, a second class of the same name is defined. It looks the same, it actually is the same, but the comparisons in the first instance will fail -- as it is, in fact, not the same class anymore.
Remember: If you develop a Python SDK tool, do not ever define global objects or classes.
Solution was to define ToolMode as "part" of AyxPlugin:
class AyxPlugin:
class ToolMode(Enum):
# ...
# ...
self.tool_mode = self.ToolMode.NONE_MODE
# etc.
Best
Christopher
Thanks for sharing this, @chrisha! I was quite stumped with your problem. I assumed (like you must have), that enums were static or singleton values like they are in every other language I've used. Very strange that it is not the case, and very good to keep in mind.