Select to view content in your preferred language

New ParameterArray container

118
6
Jump to solution
Monday
HaydenWelch
MVP Regular Contributor

Seems like the latest version of arcpy has added a ParameterArray container for parameters! I can't seem to find the documentation for it, but it definitely adopts the string based indexing that I've been using for years now haha. Glad to see that this is finally being added officially, but it would be nice to have some documentation on how the parameters are accessed by string (name, displayName, either, case sensitive, etc.).

Additionally, is this something that I have to explicitly create? Or does the getParameterInfo function return this by default? It's not implemented in Python, so knowing if I need to explicitly cast to this would be nice. Here's my sample with the explicit construction:

 

from typing import Any
from arcpy import Parameter, ParameterArray

class Tool:
    def getParameterInfo(self) -> ParameterArray:
        return ParameterArray(
            [
                Parameter(name='my_parameter', displayName='My Parameter')
            ]
        )
    
    def execute(self, parameters: ParameterArray, messages: Any) -> None:
        by_name = parameters['my_parameter']
        by_display = parameters['My Parameter']
        by_case_insensitive = parameters['MY_PARAMETER'] or parameters['my parameter']

  

0 Kudos
1 Solution

Accepted Solutions
JoshuaBixby
MVP Esteemed Contributor

If you look closer at the __init__.py file, the class is not defined at the module level but within a specific function, so it cannot be imported and used directly from ArcPy.  If you want to interact with it, you have to go through the object returned from the function.

>>> import arcpy
>>> buffer_params = arcpy.GetParameterInfo("Buffer")
>>> type(buffer_params)
<class 'arcpy.GetParameterInfo.<locals>.ParameterArray'>
>>>

View solution in original post

6 Replies
DanPatterson
MVP Esteemed Contributor

well... perhaps a different arcpy 3.7 !?  (everything is updated)

Basic version, we don't get Advanced anymore for the MVP stuff.  Enterprise?  Mine is plain simple standalone

from arcpy import Parameter, ParameterArray
Traceback (most recent call last):
  File "<string>", line 1, in <module>
ImportError: cannot import name 'ParameterArray' from 'arcpy' (C:\Arcpro_37\Resources\ArcPy\arcpy\__init__.py)

ParameterArry doesn't even list with a dir(arcpy)


... sort of retired...
HaydenWelch
MVP Regular Contributor

Hmmm, that's odd (I'm also using the Basic, tho I never got the MVP license and had to pay for a personal one). I only just now noticed it when my language server suggested it while importing Parameter: 

HaydenWelch_0-1783362864291.png

I also get an import error when I actually import it, but the name exists in the __init__.py*i* file meaning that it's an interface for something in the C code. Possibly just accidentally published to the public API before it was implemented?

0 Kudos
JoshuaBixby
MVP Esteemed Contributor

If you look closer at the __init__.py file, the class is not defined at the module level but within a specific function, so it cannot be imported and used directly from ArcPy.  If you want to interact with it, you have to go through the object returned from the function.

>>> import arcpy
>>> buffer_params = arcpy.GetParameterInfo("Buffer")
>>> type(buffer_params)
<class 'arcpy.GetParameterInfo.<locals>.ParameterArray'>
>>>
HaydenWelch
MVP Regular Contributor

That makes more sense, they must have just added that to the root typestub recently, as I've never seen it before.

Was kinda hoping that they had an official api for named parameter access that could be used in Python tools lol.

 

Edit:

I'm not sure if this is a new thing? It does seem to allow you to access by name. It also stores the named parameters as attributes on the list subclass... So if you have a name that collides with a list method it will overwrite it??

 

>>> # copy/paste of ParameterArray class from GetParameterInfo
>>> p = ParameterArray({'pop': arcpy.Parameter('pop')})
>>> p.pop()
TypeError: 'Parameter' object is not callable

 

Edit2:

So yes, it does seem that arcpy.gp.getParameterInfo just pulls the Parameter.name attribute...

>>> arcpy.gp.getParameterInfo('Clip_management')
{'in_raster': <Parameter object at ...>,
 0: <Parameter object at ...>,
 'rectangle': <Parameter object at ...>,
 1: <Parameter object at ...>,
 'out_raster': <Parameter object at ...>,
 2: <Parameter object at ...>,
 'in_template_dataset': <Parameter object at ...>,
 3: <Parameter object at ...>,
 'nodata_value': <Parameter object at ...>,
 4: <Parameter object at ...>,
 'clipping_geometry': <Parameter object at ...>,
 5: <Parameter object at ...>,
 'maintain_clipping_extent': <Parameter object at ...>,
 6: <Parameter object at ...>}

 

Here's a minimal test case toolbox that creates a totally invalid ParameterArray (I'm leaving out any dunders, but if someone uses __getitem__ as a parameter name, it will make the ParameterArray totally unusable):

from arcpy import AddMessage, GetParameterInfo, Parameter

class Toolbox:
    def __init__(self) -> None:
        self.label = 'BustedToolbox'
        self.alias = 'Busted Toolbox'
        self.tools = [Tool]


class Tool:
    def __init__(self) -> None:
        self.label = 'Broken Tool'

    def getParameterInfo(self) -> list[Parameter]:
        return [
            Parameter('pop','pop', parameterType='Optional'),
            Parameter('append','append', parameterType='Optional'),
            Parameter('clear','clear', parameterType='Optional'),
            Parameter('copy','copy', parameterType='Optional'),
            Parameter('count','count', parameterType='Optional'),
            Parameter('extend','extend', parameterType='Optional'),
            Parameter('index','index', parameterType='Optional'),
            Parameter('insert','insert', parameterType='Optional'),
            Parameter('remove','remove', parameterType='Optional'),
            Parameter('reverse','reverse', parameterType='Optional'),
            Parameter('sort','sort', parameterType='Optional'),
        ]
    
    def execute(self, parameters: list[Parameter], messages: object) -> None:
        params = GetParameterInfo()
        AddMessage(str(params))
        params.sort(key=lambda p: p.name)
        AddMessage(str(params[0].value))

Here's the version of this I've been using without issues for a couple years now. It's pretty barebones, but it doesn't overwrite instance attributes with Parameter objects (I use clear, sort, reverse, index, count, copy, and append as parameter names all the time!)

0 Kudos
DanPatterson
MVP Esteemed Contributor

Joshua.. Good to know, I thought there were stripped down versions of arcpy to go with the stripped down license levels of Pro.  The Basic level isn't much use for the stuff I want to work on (free or paid for).


... sort of retired...
JoshuaBixby
MVP Esteemed Contributor

Yeah, changing the licensing level from Advanced to Basic was a bit excessive.  It is a trade-off going from Advanced to Standard, but going to Advanced to Basic really devalues the award/reward and personal use program.