The Arcpy documentation seems to suggest that when writing python scripts that utilize/modify variables in arcpy.env
, you should wrap everything in a bunch of try/except/(finally)
loops with custom error types (which are generally a bad practice, IMO).
In the case here, they don't even guard against errors leaving the extension checked out:
import arcpy
import arcpyproduction
# Check out Production Mapping license
arcpy.CheckOutExtension("Foundation")
# Define map document, data frame, and polygon geometry coordinates
[snip]
# Create polygon geometry
for coordPair in coordList:
x, y = coordPair.split(";")
pnt = arcpy.Point(x,y)
array.add(pnt)
array.add(array.getObject(0))
boundaryPolygon = arcpy.Polygon(array)
[snip]
# Check in extension
arcpy.CheckInExtension("Foundation")
I know that's not a huge risk, but there's got to be a better way, right?
-
For the curious, yes, you can post a question that you know the answer to. +1Paul– Paul2015年08月18日 19:25:10 +00:00Commented Aug 18, 2015 at 19:25
1 Answer 1
Current versions of ArcGIS Desktop and Pro have the arcpy.EnvManager
Previously you had to use context managers. In particular, I have three for
arcpy.env.workspace
,arcpy.env.overwriteOutput
, andarcpy.Check[In|Out]Extension
class Extension(object):
def __init__(self, name):
self.name = name
def __enter__(self):
if arcpy.CheckExtension(self.name) == "Available":
arcpy.CheckOutExtension(self.name)
else:
raise ValueError("%s license isn't available" % self.name)
def __exit__(self, *args):
arcpy.CheckInExtension(self.name)
class OverwriteState(object):
def __init__(self, overwrite):
self.orig_state = arcpy.env.overwriteOutput
self.new_state = bool(overwrite)
def __enter__(self, *args, **kwargs):
arcpy.env.overwriteOutput = self.new_state
def __exit__(self, *args, **kwargs):
arcpy.env.overwriteOutput = self.orig_state
class WorkSpace(object):
def __init__(self, path):
self.orig_workspace = arcpy.env.workspace
self.new_workspace = path
def __enter__(self, *args, **kwargs):
arcpy.env.workspace = self.new_workspace
def __exit__(self, *args, **kwargs):
arcpy.env.workspace = self.orig_workspace
If your version of arcpy is recent enough to be on Python 2.7, you can use them all at the same time like this:
with Extension("3D"), OverwriteState(True), Workspace("c:/GrosMorne"):
arcpy.HillShade_3d("WesternBrook", "wbrook_hill", 300)
arcpy.Aspect_3d("WesternBrook", "wbrook_aspect")
If you're stuck on Python 2.6, it's a little uglier:
with Extension("3D"):
with OverwriteState(True):
with: Workspace("c:/GrosMorne"):
arcpy.HillShade_3d("WesternBrook", "wbrook_hill", 300)
arcpy.Aspect_3d("WesternBrook", "wbrook_aspect")
These guard against errors in whatever processing you do so that the environment variables/extensions are reverted back to their original state no matter how the code within the with
block exits (e.g., successfully, errored, interrupted).
That looks something like this:
arcpy.env.overwriteOutput = False
print('Before the context manager: {}'.format(arcpy.env.overwriteOutput))
with OverwriteState(True):
print('Inside the context manager: {}'.format(arcpy.env.overwriteOutput))
raise ValueError("arcpy is a bad time")
print('After the context manager: {}'.format(arcpy.env.overwriteOutput))
Output:
Before the context manager: False
Inside the context manager: True
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
[snip]
ValueError: arcpy is a bad time
---------------------------------------------------------------------------
After the context manager: False
After learning about some python standard library goodness, the context managers simplify to:
import arcpy
from contextlib import contextmanager
@contextmanager
def Extension(name):
""" Safely use ArcGIS extensions """
if arcpy.CheckExtension(name) == u"Available":
status = arcpy.CheckOutExtension(name)
yield status
else:
raise RuntimeError("%s license isn't available" % name)
arcpy.CheckInExtension(name)
@contextmanager
def OverwriteState(state):
""" Temporarily set the ``overwriteOutput`` env variable. """
orig_state = arcpy.env.overwriteOutput
arcpy.env.overwriteOutput = bool(state)
yield state
arcpy.env.overwriteOutput = orig_state
@contextmanager
def WorkSpace(path):
""" Temporarily set the ``workspace`` env variable. """
orig_workspace = arcpy.env.workspace
arcpy.env.workspace = path
yield path
arcpy.env.workspace = orig_workspace
-
I really like this context manager-based approach - I had some functions I was using to get the same results, but this is more elegant. I wanted it generic though, so I wrote a context manager for all of the environment settings. gist.github.com/nickrsan/… - it seemed like enough of a shift that I shouldn't edit it into the answer though, but thought it was worth leaving here. Thanks for the great idea!nicksan– nicksan2017年06月03日 18:19:20 +00:00Commented Jun 3, 2017 at 18:19
-
@nicksan -- I think that's a great approach and totally belongs in this answer! I posted it as a wiki for a reason ;)Paul H– Paul H2017年06月04日 15:23:58 +00:00Commented Jun 4, 2017 at 15:23