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.
Solved! Go to Solution.
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)
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)
Fascinating, i would try to keep an eye out.