I have had this never-ending question regarding how to run a Python Script to make updates to Versioned data.
How can I enter an Edit Session, on a pre-existing version, complete the edit session, review the changes, then reconcile and post WITHOUT causing a schema lock.
For some background, the below script is not in it's entirety:
I have created a database connection that is pointing to the intended, pre-existing version. The update cursor reads and updates a feature class using the above database connection. The update cursor completes successfully, updating correctly.
It is intended that the user will then manually review the changes, if the changes are good, reconcile and post. If bad, delete the version (I need a better process for this as well).
The Problem:
If the user enters an ArcMap Editor Toolbar Edit Session post update, checks everything, reconciles and posts. It works, no locks. Then, if you run the tool again, SystemError: error return without exception set. If I go to the Version Manager to view current locks, there is a lock on that version even after successful rec/post.
How does one prevent this lock from occurring. The python editing process is not documented in depth enough, and does not touch on how the delta tables are affected (I have tried to research the ArcObjects docs, which are mostly copied and pasted into arcpy). Why would I have schema locks if I am editing in one toolset vs. the other, but they're taking place after operations? I have tried many workarounds, numerous times, yet I am clearly missing something.
"""
https://gis.stackexchange.com/a/158627
"""
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)
"""
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
SPLIT = filter(None, path.split(os.sep))
if path.startswith('\\\\'):
SPLIT[0] = r'\\{0}'.format(SPLIT[0])
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 EditorUpdateCursor(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):
"""init 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):
if traceback is None:
self.edit.stopOperation()
self.edit.stopEditing(True)
self.edit = None
else:
self.edit.abortOperation()
self.edit.stopEditing(False)
self.edit = None
if updatedStructuresCount > 0:
arcpy.AddMessage('\nPole Numbers are being updated...')
for structure in structureDict:
where = """GLOBALID = '{}'""".format(structure)
with editUpdCursor.EditorUpdateCursor(strPath, ['POLENUMBER'], where) as cursor:
for row in cursor:
newPoleNum = structureDict.get(structure)
row[0] = newPoleNum
cursor.updateRow(row)
arcpy.AddMessage('Pole Numbers update complete...')