[GIS] Using arcpy.da.Editor correctly

arcpyediting

I am attempting to remove duplicates in a feature class that are generated by a Python script. At the end of my script I have the code block below; I haven't implemented it yet, but would like to see if this is the correct way to use arcpy.da.Editor as the errors that I have been receiving are in regards to being outside of an edit session.

Is arcpy.da.Editor being used correctly here?

`

        fields = ['OBJECTID', 'SRNumber', 'ItemLoc_9']

        table_rows = []
        delete_rows = []

        where_clause =  """ ItemLoc_9 = ' '"""

        with arcpy.da.Editor(appendClass) as edit:
            with arcpy.da.SearchCursor(appendClass, fields, where_clause) as cursor:
                for row in cursor:
                    if [row[1], row[2]] in table_rows:
                        delete_rows.append(row[0])
                    else:
                        table_rows.append([row[1], row[2]])

                del table_rows

        with arcpy.da.UpdateCursor(appendClass, ['OBJECTID']) as cursor2:
            for row in cursor2:
                if row[0] in delete_rows:
                    print 'Deleting record: OBJECTID = ' + str(row[0])
                    cursor2.deleteRow()

                    print '\ndone'`

Best Answer

I have had lots of trouble/inconsistencies with this in the past. Instead, I have implemented some wrappers for both the update and insert cursors. This way, every time I run an update cursor, I know an edit session is properly started and stopped for the cursor. Here is my update cursor (with a helper function to get the workspace):

import arcpy
import os

def find_ws(path, ws_type=''):
    """finds a valid workspace path for an arcpy.da.Editor() Session

    Required:
        path -- path to features or workspace

    Optional:
        ws_type -- option to find specific workspace type (FileSystem|LocalDatabase|RemoteDatabase)

    """
    # try original path first
    if os.sep not in path:
        path = arcpy.Describe(path).catalogPath
    desc = arcpy.Describe(path)
    if hasattr(desc, 'workspaceType'):
        if ws_type and ws_type == desc.workspaceType:
            return path
        elif not ws_type:
            return path

    # search until finding a valid workspace
    SPLIT = filter(None, path.split(os.sep))
    if path.startswith('\\\\'):
        SPLIT[0] = r'\\{0}'.format(SPLIT[0])

    # find valid workspace
    for i in xrange(1, len(SPLIT)):
        sub_dir = os.sep.join(SPLIT[:-i])
        desc = arcpy.Describe(sub_dir)
        if hasattr(desc, 'workspaceType'):
            if ws_type and ws_type == desc.workspaceType:
                return sub_dir
            elif not ws_type:
                return sub_dir

class UpdateCursor(object):
    """wrapper class for arcpy.da.UpdateCursor, to automatically
    implement editing (required for versioned data, and data with
    geometric networks, topologies, network datasets, and relationship
    classes"""
    def __init__(self, *args, **kwargs):
        """initiate wrapper class for update cursor.  Supported args:

        in_table, field_names, where_clause=None, spatial_reference=None,
        explode_to_points=False, sql_clause=(None, None)
        """
        self.args = args
        self.kwargs = kwargs
        self.edit = None

    def __enter__(self):
        ws = None
        if self.args:
            ws = find_ws(self.args[0])
        elif 'in_table' in self.kwargs:
            ws = find_ws(self.kwargs['in_table'])
        self.edit = arcpy.da.Editor(ws)
        self.edit.startEditing()
        self.edit.startOperation()
        return arcpy.da.UpdateCursor(*self.args, **self.kwargs)

    def __exit__(self, type, value, traceback):
        self.edit.stopOperation()
        self.edit.stopEditing(True)
        self.edit = None

So if you save this somewhere in your PYTHONPATH and call the update cursor, you can always be sure that an edit session is started and stopped. This wrapper also allows the usage of the with statement for the cursor.

If you saved the script above as some_module.py and save it in your PYTHONPATH, you can call it like this:

import some_module

fc = r'C:\Path\to_your\FileGeodatabase\YourFC' 
fields = ["field1", "field2"]

with some_module.UpdateCursor(fc, fields) as rows:
    for row in rows:
        row[1] = row[0]
        rows.updateRow(row)
Related Question