pathlib seems to be the preferred method for working with system paths in Python 3. We've started using it in place of `os.path` in our office recently and really like it. However, we've run into problems with using it with arcpy. For example:
workspace = pathlib.Path('C:\some path') \ 'anotherfolder' \ 'connection.sde'
with arcpy.EnvManager(workspace=workspace):
passThrows this error: "RuntimeError: Object: Error in accessing environment <workspace>"
To work around this, we end up wrapping all of our Path object with str(). For example:
workspace = str(pathlib.Path('C:\some path') \ 'anotherfolder' \ 'connection.sde')
with arcpy.EnvManager(workspace=workspace):
passIt would be great if arcpy (Pro version) handled these Path objects natively!
Per the arcpy team at the Dev Summit this is releasing with 3.7. All tools will work with path objects as inputs. An env setting needs to be set for outputs.
Congratulations! The idea made its way into the software - geoprocessing tools and ArcPy functionality support Python pathlib.Path objects, allowing paths to be passed directly without being converted to strings.
Learn more about using pathlib.Path with geoprocessing tools
Currently, pathlib.Path is unsupported by arcpy objects and functions that take filepath args.
Since pathlib.Path is common and can almost always be safely converted to a string path with str(path) or str(path.resolve()) or path.resolve().__fspath__(), I think it should be okay to cast it to a string in the conversion function:
def gp_fixarg(arg, string_results, pass_arc_object):
from arcpy.arcobjects import Result
from pathlib import Path
if isinstance(arg, Result) and string_results:
return str(arg._arc_object)
if isinstance(arg, (tuple, list)):
return gp_fixargs(arg)
if hasattr(arg, '_arc_object') and pass_arc_object:
return arg._arc_object
if isinstance(arg, Path):
return str(arg.resolve())
return arg
This can also be monkey patched if you don't want to edit the _base module:
import arcpy
def allow_pathlib_path(func):
from pathlib import Path
def _wrapped(arg, *flags):
if isinstance(arg, Path):
return str(arg.resolve())
return func(arg, *flags)
return _wrapped
arcpy.geoprocessing._base.gp_fixarg = allow_pathlib_path(arcpy.geoprocessing._base.gp_fixarg)
Also since path resolution can be slow, you could wrap the inner function in an lru_cache decorator (so if a Path object is passed in a tight loop, it won't be resolved each time)
import arcpy
from pathlib import Path
from functools import lru_cache
def allow_pathlib_path(func):
@lru_cache
def _wrapped(arg, *flags):
if isinstance(arg, Path):
return str(arg.resolve())
return func(arg, *flags)
return _wrapped
arcpy.geoprocessing._base.gp_fixarg = allow_pathlib_path(arcpy.geoprocessing._base.gp_fixarg)
This may be supported now in 3.7?
ArcPy
Geoprocessing tools and ArcPy functionality support Pythonpathlib.Pathobjects, allowing paths to be passed directly without being converted to strings.
Learn more about using pathlib.Path with geoprocessing tools
@AlfredBaldenweck Awesome I'm still on 3.5 so I've been using the monkey patch since everything I write uses pathlib lol. I wonder if they just did exactly this...
It is definitely there, line 2739 and beyond
C:\...your_install_folder...\Resources\ArcPy\arcpy\__init__.py
# =============================================================================
# Pathlib support: Enable pathlib.Path objects to work with all ArcPy functions
# =============================================================================
@DanPatterson I really need to update, the str(Path(...) / ... / ...) workaround was starting to get painful
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.