Select to view content in your preferred language

Allow user to select if existing file is deleted in Python Toolbox

793
1
08-19-2022 12:52 PM
Status: Closed
dslamb2022
Occasional Contributor

Within a Python Toolbox, using a parameter type DEFile and direction set to output, when you choose an existing file ArcGIS Pro automatically deletes the file before execution. I would like the option to append to an existing file. Using the environment manager to set overwriteOutput to False, causes an error. Suppressing this error does not work. Changing the direction to input does not work, because you need an existing file. I want option to create a new file or modify an existing one.

1 Comment
HannesZiegler
Status changed to: Closed

Thank you for sharing your idea! As I understand it, you wish to create a tool that allows the user to specify a file, and whether or not to append to the file or to overwrite its contents. If the specified file does not exist, you wish for the tool to create that file and write contents to it. What you are suggesting is already possible within the existing framework for the Python Toolbox. You will need to separate the input and output parameters and derive the output from the input. For input, DEFile may not be the best choice, as it does not allow you to select a file that does not yet exist. You can, however, specify the output to be of type DEFile. The below Python toolbox code showcases two approaches: Approach (A) allows overwriting or appending to the selected file, and approach (B) allows overwriting, appending to, or creating and writing to a new file if the specified file does not already exist:

 

# -*- coding: utf-8 -*-

import os

import arcpy


class Toolbox:
    def __init__(self):
        """
        This toolbox showcases how you can accomplish overwriting or appending content to a file.
        """
        self.label = "Toolbox"
        self.alias = "toolbox"

        # List of tool classes associated with this toolbox
        self.tools = [A_OverwriteOrApendFile, B_OverwriteApendOrCreateFile]


class A_OverwriteOrApendFile:
    def __init__(self):
        """
        This tool appends to or overwrites an existing file, but does not have the ability to creating a new file.
        """
        self.label = "(A) Overwrite or Append file with contents"
        self.description = "This tool appends to or overwrites an existing file, but does not allow creating a new file."

    def getParameterInfo(self):
        """Define the tool parameters."""
        params = [
            arcpy.Parameter(
                name="infile",
                displayName="File",
                datatype="DEFile",
                direction="Input",
                parameterType="Required",
            ),
            arcpy.Parameter(
                name="text",
                displayName="Write Text",
                datatype="GPString",
                direction="Input",
                parameterType="Required",
            ),
            arcpy.Parameter(
                name="append",
                displayName="Append",
                datatype="GPBoolean",
                direction="Input",
                parameterType="Optional",
            ),
            arcpy.Parameter(
                name="output",
                displayName="Output File",
                datatype="DEFILE",
                direction="Output",
                parameterType="Derived"
            )
        ]
        return params

    def isLicensed(self):
        """Set whether the tool is licensed to execute."""
        return True

    def updateParameters(self, parameters):
        """Modify the values and properties of parameters before internal
        validation is performed.  This method is called whenever a parameter
        has been changed."""
        return

    def updateMessages(self, parameters):
        """Modify the messages created by internal validation for each tool
        parameter. This method is called after internal validation."""
        return

    def execute(self, parameters, messages):
        """The source code of the tool."""
        infile      = parameters[0].valueAsText  
        text        = parameters[1].valueAsText
        append      = parameters[2].value
        output      = parameters[3]

        # append or overwrite
        if append:
            with open(infile, "at") as f:
                f.write(text)
        else:
            with open(infile, "wt") as f:
                f.write(text)
        output.value = infile
        return

    def postExecute(self, parameters):
        """This method takes place after outputs are processed and
        added to the display."""
        return


class B_OverwriteApendOrCreateFile:
    def __init__(self):
        """
        This tool appends to or overwrites an existing file, or creates and writes to a new file
        if the specified file does not yet exist.
        """
        self.label = "(B) Overwrite, Append, or Create file with contents"
        self.description = "This tool appends to or overwrites an existing file, or creates and writes to a new file."

    def getParameterInfo(self):
        """Define the tool parameters."""
        params = [
            arcpy.Parameter(
                name="dir",
                displayName="Directory",
                datatype="DEFolder",
                direction="Input",
                parameterType="Required",
            ),
            arcpy.Parameter(
                name="file",
                displayName="File",
                datatype="GPString",
                direction="Input",
                parameterType="Required",
            ),
            arcpy.Parameter(
                name="text",
                displayName="Write Text",
                datatype="GPString",
                direction="Input",
                parameterType="Required",
            ),
            arcpy.Parameter(
                name="append",
                displayName="Append",
                datatype="GPBoolean",
                direction="Input",
                parameterType="Optional",
            ),
            arcpy.Parameter(
                name="output",
                displayName="Output File",
                datatype="DEFILE",
                direction="Output",
                parameterType="Derived"
            )
        ]
        return params

    def isLicensed(self):
        """Set whether the tool is licensed to execute."""
        return True

    def updateParameters(self, parameters):
        """Modify the values and properties of parameters before internal
        validation is performed.  This method is called whenever a parameter
        has been changed."""
        indir = parameters[0]
        if indir.altered:  # list existing files in selected directory
            infile             = parameters[1]
            infile.filter.type = "ValueList"
            infile.filter.list = [
                file
                for file
                in os.listdir(indir.valueAsText)
                if os.path.isfile(os.path.join(indir.valueAsText, file))
            ]
            if infile.altered:  # allow user to specify their own (new) file
                infile.filter.list = list(set(infile.filter.list + [infile.valueAsText]))
        return

    def updateMessages(self, parameters):
        """Modify the messages created by internal validation for each tool
        parameter. This method is called after internal validation."""
        return

    def execute(self, parameters, messages):
        """The source code of the tool."""
        indir       = parameters[0].valueAsText
        filename    = parameters[1].valueAsText
        filepath    = os.path.join(indir, filename)    
        text        = parameters[2].valueAsText
        append      = parameters[3].value
        output      = parameters[4]

        # append or overwrite
        # NOTE: Python's open func. will create new file if it does not exist
        if append:
            with open(filepath, "at") as f:
                f.write(text)
        else:
            with open(filepath, "wt") as f:
                f.write(text)
        output.value = filepath
        return

    def postExecute(self, parameters):
        """This method takes place after outputs are processed and
        added to the display."""
        return

 

Since the proposed idea is already achievable with the existing tooling, we have closed this idea as "already offered". You may use the attached toolbox as a reference for implementing your own tools with these capabilities. Thank you again for sharing your idea, we appreciate your suggestion, and we are always looking for ways to improve our capabilities. If you have any further questions or concerns, please do not hesitate to contact us.