Select to view content in your preferred language

Suggestion on setting up python environment for developing toolbox

333
2
Jump to solution
07-04-2024 01:53 AM
nomit
by
New Contributor

Hi All,

I was looking for ways in which i can setup my working environment for developing toolboxes in arcgis pro. Currently i am using vscode and use arcpy.AddMessage method to print out messages after the tool's execution. Not sure whether this is the recommended way or are there any better ways in which this can be done.

On a side note i like to keep arcgis pro open just to check the output inbetween.

If the community already has similar discussion please do point out. I am new to this.

1 Solution

Accepted Solutions
HaydenWelch
Frequent Contributor

I've actually been working on a framework for managing toolboxes (called pytframe/pytframe2 for now)

The module structure is subject to change, but I've currently got "archelp" and "models" files that contain helper functions (one that overrides print with arcpy.AddMessage and still prints to stdout)

The models file contains some wrapper classes for interacting with tables and feature classes a bit more naturally. I've got some example tools in the production and dev toolboxes as well.

I've been using this dynamic reloading system for about 2 years now and it's great. I either have everyone using the toolbox from a single file on a network share repo, or have them clone the repo down to a system folder (C:/repos/arcpy-tools or something so templated projects can always find their local clone, or the toolbox stays referenced when someone else opens their project)

If you want any more detailed explanations or examples don't be afraid to message me, I'll be continually developing and documenting this system when I get the time.

Here's a snippet for overwriting the print function to support the ArcPro message queue:

import builtins

def print(*values: object,
          sep: str = " ",
          end: str = "\n",
          file = None,
          flush: bool = False,
          severity: Literal['INFO', 'WARNING', 'ERROR'] = "INFO"):
    """ Print a message to the ArcGIS Pro message queue and stdout
    set severity to 'WARNING' or 'ERROR' to print to the ArcGIS Pro message queue with the appropriate severity
    """
    # Join the values into a single string
    message = f"{sep.join(map(str, values))}"
    
    # Print the message to stdout
    builtins.print(f"{severity+': ' if severity != 'INFO' else ''}{sep.join(values)}", sep=sep, end=end, file=file, flush=flush)
    
    # Print the message to the ArcGIS Pro message queue with the appropriate severity
    match severity:
        case "WARNING":
            arcpy.AddWarning(f"{message}")
        case "ERROR":
            arcpy.AddError(f"{message}")
        case _:
            arcpy.AddMessage(f"{message}")
    return

 

And one of the most useful addin methods I use (only 2 lines of code too!):

def as_dict(cursor: SearchCursor | UpdateCursor) -> Generator[dict[str, Any], None, None]:
    """Convert a search cursor or update cursor to an iterable dictionary generator
    This allows for each row operation to be done using fieldnames as keys.
    
    Arguments:
        cursor: search cursor or update cursor.
        
    Yields:
        dictionary of the cursor row.
    
    NOTE: This function will not overwrite the cursor object
    if used in a context manager and iterating through the yielded
    dictionaries will progress the cursor as if you were iterating
    through the cursor object itself.
    
    usage:
    >>> with SearchCursor(...) as cursor:
    >>>     for row in as_dict(cursor):
    >>>         print(row)
    ------------------------------------------------
    >>> with UpdateCursor(...) as cursor:
    >>>     for row in as_dict(cursor):
    >>>        row["field"] = "new value"
    >>>        cursor.updateRow(list(row.values()))
    """
    yield from (dict(zip(cursor.fields, row)) for row in cursor)

 

View solution in original post

2 Replies
HaydenWelch
Frequent Contributor

I've actually been working on a framework for managing toolboxes (called pytframe/pytframe2 for now)

The module structure is subject to change, but I've currently got "archelp" and "models" files that contain helper functions (one that overrides print with arcpy.AddMessage and still prints to stdout)

The models file contains some wrapper classes for interacting with tables and feature classes a bit more naturally. I've got some example tools in the production and dev toolboxes as well.

I've been using this dynamic reloading system for about 2 years now and it's great. I either have everyone using the toolbox from a single file on a network share repo, or have them clone the repo down to a system folder (C:/repos/arcpy-tools or something so templated projects can always find their local clone, or the toolbox stays referenced when someone else opens their project)

If you want any more detailed explanations or examples don't be afraid to message me, I'll be continually developing and documenting this system when I get the time.

Here's a snippet for overwriting the print function to support the ArcPro message queue:

import builtins

def print(*values: object,
          sep: str = " ",
          end: str = "\n",
          file = None,
          flush: bool = False,
          severity: Literal['INFO', 'WARNING', 'ERROR'] = "INFO"):
    """ Print a message to the ArcGIS Pro message queue and stdout
    set severity to 'WARNING' or 'ERROR' to print to the ArcGIS Pro message queue with the appropriate severity
    """
    # Join the values into a single string
    message = f"{sep.join(map(str, values))}"
    
    # Print the message to stdout
    builtins.print(f"{severity+': ' if severity != 'INFO' else ''}{sep.join(values)}", sep=sep, end=end, file=file, flush=flush)
    
    # Print the message to the ArcGIS Pro message queue with the appropriate severity
    match severity:
        case "WARNING":
            arcpy.AddWarning(f"{message}")
        case "ERROR":
            arcpy.AddError(f"{message}")
        case _:
            arcpy.AddMessage(f"{message}")
    return

 

And one of the most useful addin methods I use (only 2 lines of code too!):

def as_dict(cursor: SearchCursor | UpdateCursor) -> Generator[dict[str, Any], None, None]:
    """Convert a search cursor or update cursor to an iterable dictionary generator
    This allows for each row operation to be done using fieldnames as keys.
    
    Arguments:
        cursor: search cursor or update cursor.
        
    Yields:
        dictionary of the cursor row.
    
    NOTE: This function will not overwrite the cursor object
    if used in a context manager and iterating through the yielded
    dictionaries will progress the cursor as if you were iterating
    through the cursor object itself.
    
    usage:
    >>> with SearchCursor(...) as cursor:
    >>>     for row in as_dict(cursor):
    >>>         print(row)
    ------------------------------------------------
    >>> with UpdateCursor(...) as cursor:
    >>>     for row in as_dict(cursor):
    >>>        row["field"] = "new value"
    >>>        cursor.updateRow(list(row.values()))
    """
    yield from (dict(zip(cursor.fields, row)) for row in cursor)

 

nomit
by
New Contributor

Fascinating, i would try to keep an eye out.