Skip to main content

Hello all,

 

I've been trying to delete a folder containing some data after processing. I cannot use a temp folder so have turned to python to complete the task.

I've tried following the comments in the following thread but it doesn't seem to work for me File Copy - DELETE (safe.com)

I can get the process to work when 'hard coding' as shown below.

image 

But when I try to make it dynamic by replacing 'location' with feature.getAttribute('filepath') and dir with feature.getAttribute('directory') it fails.

The filepath and directory attributes are shown below:

'filepath' = C:\\Project\\FME\\processing\\data

'directory' = 20230925

image

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:

  1. Connect to esri portal
  2. download a copy of a feature class including attachments
  3. check if the feature class has been updated since last downloaded
    1. if no change delete download
    2. 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:

  1. My connection to portal occasionally needs reauthenticating and there doesn't appear to be a way to store connection details to automate reauthentication.
  2. 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).

image 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.


Reply