5

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?

asked Aug 18, 2015 at 18:47
1

1 Answer 1

6

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, and
  • arcpy.Check[In|Out]Extension

They are:

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
2
  • 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! Commented 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 ;) Commented Jun 4, 2017 at 15:23

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.