One question, why can't you use the temppathnamecreator?
Re the python, if you print the path variable what is the value? sometimes i do find that parameters can have some weird encoding when assigned to a variable.
try assigning the variable like this:
location = f"{feature.getAttribute('filepath')}"
You have the indentation incorrect in the python code, and if you use the class interface you need to have the init/input/close functions. Try something like this instead:
import os
import shutil
def delete_folder(f):
# location
location = f.getAttribute('filepath')
# directory
dir = f.getAttribute('directory')
path = os.path.join(location, dir)
# removing directory
shutil.rmtree(path)
and in the pythoncaller you have to refer to this function
Hi @hkingsbury thank you for your reply. I'm getting the error "Python Exception <NameError>: name 'feature' is not defined".
I've tried adding the code to define "feature" based on the default text in the python caller as well as the reply in the post here but this hasn't worked.
With regards to your query on temppathnamecreator. The process that I'm trying to automate (using task scheduler) is as follows:
- Connect to esri portal
- download a copy of a feature class including attachments
- check if the feature class has been updated since last downloaded
- if no change delete download
- if change do nothing
Points 1 & 2 are currently being done in a python script. I've tried to do this in FME but I've had two issues:
- My connection to portal occasionally needs reauthenticating and there doesn't appear to be a way to store connection details to automate reauthentication.
- Attachments to the feature class can be .doc, pdf, xlsx etc. While it's possible to decode some of the attachments to access them, it doesn't appear to be possible to decode all file formats.
I have a workbench that does point 3.
Points 3.1 and 3.2 are what I'm struggling to get the python caller to do.
Hi @nordpil thank you for your reply. The workbench is now showing as completing successfully but the folders and files in the directory are not being deleted.
but do you get an error message? try executing the delete from a python prompt (outside FME) and see what happens. I bet there is a process (e.g. FME) that has a handle/lock open on the folder, and you can't delete it for that reason...
but do you get an error message? try executing the delete from a python prompt (outside FME) and see what happens. I bet there is a process (e.g. FME) that has a handle/lock open on the folder, and you can't delete it for that reason...
There are no error messages generated in FME.
Trying the same code in ArcPro (replacing the attribute derived folder path, with the folder path) also doesn't delete the folder.
However, going back tothe code in my first screenshot does delete the folder.
I've now managed to work round using the python caller. My workflow is:
AttributeFileWriter generates a .py file using the code text and dynamic folder path attribute (as below).
I then use a SystemCaller to run a batch file that calls the .py file.
Not the neatest execution I'm sure but gets the job done.
Thanks @nordpil and @hkingsbury for your responses.
You have the indentation incorrect in the python code, and if you use the class interface you need to have the init/input/close functions. Try something like this instead:
import os
import shutil
def delete_folder(f):
# location
location = f.getAttribute('filepath')
# directory
dir = f.getAttribute('directory')
path = os.path.join(location, dir)
# removing directory
shutil.rmtree(path)
and in the pythoncaller you have to refer to this function
Kicking myself I missed that indentation. @scarecrow this should work, you shouldn't need to be writing a .py file and calling it with a systemcaller
For completeness, heres the same code from @nordpil but in the class
import fme
import fmeobjects
import os, shutil
class FeatureProcessor(object):
"""Template Class Interface:
When using this class, make sure its name is set as the value of the 'Class
to Process Features' transformer parameter.
"""
def __init__(self):
"""Base constructor for class members."""
pass
def input(self, feature):
# location
location = f.getAttribute('filepath')
# directory
dir = f.getAttribute('directory')
path = os.path.join(location, dir)
# removing directory
shutil.rmtree(path)
self.pyoutput(feature)
def close(self):
"""This method is called once all the FME Features have been processed
from input().
"""
pass
def process_group(self):
"""When 'Group By' attribute(s) are specified, this method is called
once all the FME Features in a current group have been sent to input().
FME Features sent to input() should generally be cached for group-by
processing in this method when knowledge of all Features is required.
The resulting Feature(s) from the group-by processing should be emitted
through self.pyoutput().
FME will continue calling input() a number of times followed
by process_group() for each 'Group By' attribute, so this
implementation should reset any class members for the next group.
"""
pass
def has_support_for(self, support_type):
"""This method returns whether this PythonCaller supports a certain type.
The only supported type is fmeobjects.FME_SUPPORT_FEATURE_TABLE_SHIM.
:param int support_type: The support type being queried.
:returns: True if the passed in support type is supported.
:rtype: bool
"""
if support_type == fmeobjects.FME_SUPPORT_FEATURE_TABLE_SHIM:
# If this is set to return True, FME will pass features to the input() method that
# come from a feature table object. This allows for significant performance gains
# when processing large numbers of features.
# To enable this, the following conditions must be met:
# 1) features passed into the input() method cannot be copied or cached for later use
# 2) features cannot be read or modified after being passed to self.pyoutput()
# 3) Group Processing must not be enabled
# Violations will cause undefined behavior.
return False
return False
Kicking myself I missed that indentation. @scarecrow this should work, you shouldn't need to be writing a .py file and calling it with a systemcaller
For completeness, heres the same code from @nordpil but in the class
import fme
import fmeobjects
import os, shutil
class FeatureProcessor(object):
"""Template Class Interface:
When using this class, make sure its name is set as the value of the 'Class
to Process Features' transformer parameter.
"""
def __init__(self):
"""Base constructor for class members."""
pass
def input(self, feature):
# location
location = f.getAttribute('filepath')
# directory
dir = f.getAttribute('directory')
path = os.path.join(location, dir)
# removing directory
shutil.rmtree(path)
self.pyoutput(feature)
def close(self):
"""This method is called once all the FME Features have been processed
from input().
"""
pass
def process_group(self):
"""When 'Group By' attribute(s) are specified, this method is called
once all the FME Features in a current group have been sent to input().
FME Features sent to input() should generally be cached for group-by
processing in this method when knowledge of all Features is required.
The resulting Feature(s) from the group-by processing should be emitted
through self.pyoutput().
FME will continue calling input() a number of times followed
by process_group() for each 'Group By' attribute, so this
implementation should reset any class members for the next group.
"""
pass
def has_support_for(self, support_type):
"""This method returns whether this PythonCaller supports a certain type.
The only supported type is fmeobjects.FME_SUPPORT_FEATURE_TABLE_SHIM.
:param int support_type: The support type being queried.
:returns: True if the passed in support type is supported.
:rtype: bool
"""
if support_type == fmeobjects.FME_SUPPORT_FEATURE_TABLE_SHIM:
# If this is set to return True, FME will pass features to the input() method that
# come from a feature table object. This allows for significant performance gains
# when processing large numbers of features.
# To enable this, the following conditions must be met:
# 1) features passed into the input() method cannot be copied or cached for later use
# 2) features cannot be read or modified after being passed to self.pyoutput()
# 3) Group Processing must not be enabled
# Violations will cause undefined behavior.
return False
return False
Hi hkingsbury thank you for your reply.
I tried the code you suggested but ger the following error:
Python Exception <NameError>: name 'f' is not defined
Traceback (most recent call last):
File "<string>", line 19, in input
NameError: name 'f' is not defined
Error encountered while calling method `input'
PythonCaller (PythonFactory): PythonFactory failed to process feature
I tried adding the code from nordpil, def delete_folder(f): I assume that this is defining 'f' but this didn't work.
Kicking myself I missed that indentation. @scarecrow this should work, you shouldn't need to be writing a .py file and calling it with a systemcaller
For completeness, heres the same code from @nordpil but in the class
import fme
import fmeobjects
import os, shutil
class FeatureProcessor(object):
"""Template Class Interface:
When using this class, make sure its name is set as the value of the 'Class
to Process Features' transformer parameter.
"""
def __init__(self):
"""Base constructor for class members."""
pass
def input(self, feature):
# location
location = f.getAttribute('filepath')
# directory
dir = f.getAttribute('directory')
path = os.path.join(location, dir)
# removing directory
shutil.rmtree(path)
self.pyoutput(feature)
def close(self):
"""This method is called once all the FME Features have been processed
from input().
"""
pass
def process_group(self):
"""When 'Group By' attribute(s) are specified, this method is called
once all the FME Features in a current group have been sent to input().
FME Features sent to input() should generally be cached for group-by
processing in this method when knowledge of all Features is required.
The resulting Feature(s) from the group-by processing should be emitted
through self.pyoutput().
FME will continue calling input() a number of times followed
by process_group() for each 'Group By' attribute, so this
implementation should reset any class members for the next group.
"""
pass
def has_support_for(self, support_type):
"""This method returns whether this PythonCaller supports a certain type.
The only supported type is fmeobjects.FME_SUPPORT_FEATURE_TABLE_SHIM.
:param int support_type: The support type being queried.
:returns: True if the passed in support type is supported.
:rtype: bool
"""
if support_type == fmeobjects.FME_SUPPORT_FEATURE_TABLE_SHIM:
# If this is set to return True, FME will pass features to the input() method that
# come from a feature table object. This allows for significant performance gains
# when processing large numbers of features.
# To enable this, the following conditions must be met:
# 1) features passed into the input() method cannot be copied or cached for later use
# 2) features cannot be read or modified after being passed to self.pyoutput()
# 3) Group Processing must not be enabled
# Violations will cause undefined behavior.
return False
return False
Sorry, i butchered that code a bit, on lines 18 and 21, replace 'f' with 'feature'
Kicking myself I missed that indentation. @scarecrow this should work, you shouldn't need to be writing a .py file and calling it with a systemcaller
For completeness, heres the same code from @nordpil but in the class
import fme
import fmeobjects
import os, shutil
class FeatureProcessor(object):
"""Template Class Interface:
When using this class, make sure its name is set as the value of the 'Class
to Process Features' transformer parameter.
"""
def __init__(self):
"""Base constructor for class members."""
pass
def input(self, feature):
# location
location = f.getAttribute('filepath')
# directory
dir = f.getAttribute('directory')
path = os.path.join(location, dir)
# removing directory
shutil.rmtree(path)
self.pyoutput(feature)
def close(self):
"""This method is called once all the FME Features have been processed
from input().
"""
pass
def process_group(self):
"""When 'Group By' attribute(s) are specified, this method is called
once all the FME Features in a current group have been sent to input().
FME Features sent to input() should generally be cached for group-by
processing in this method when knowledge of all Features is required.
The resulting Feature(s) from the group-by processing should be emitted
through self.pyoutput().
FME will continue calling input() a number of times followed
by process_group() for each 'Group By' attribute, so this
implementation should reset any class members for the next group.
"""
pass
def has_support_for(self, support_type):
"""This method returns whether this PythonCaller supports a certain type.
The only supported type is fmeobjects.FME_SUPPORT_FEATURE_TABLE_SHIM.
:param int support_type: The support type being queried.
:returns: True if the passed in support type is supported.
:rtype: bool
"""
if support_type == fmeobjects.FME_SUPPORT_FEATURE_TABLE_SHIM:
# If this is set to return True, FME will pass features to the input() method that
# come from a feature table object. This allows for significant performance gains
# when processing large numbers of features.
# To enable this, the following conditions must be met:
# 1) features passed into the input() method cannot be copied or cached for later use
# 2) features cannot be read or modified after being passed to self.pyoutput()
# 3) Group Processing must not be enabled
# Violations will cause undefined behavior.
return False
return False
Thanks @hkingsbury that has worked. I really appreciate the time both you and @nordpil have taken to reply, thank you both.